@mastra/rag 0.0.0-commonjs-20250227130920
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/.turbo/turbo-build.log +23 -0
- package/CHANGELOG.md +1151 -0
- package/LICENSE +44 -0
- package/README.md +26 -0
- package/dist/_tsup-dts-rollup.d.cts +620 -0
- package/dist/_tsup-dts-rollup.d.ts +620 -0
- package/dist/index.cjs +2524 -0
- package/dist/index.d.cts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +2505 -0
- package/docker-compose.yaml +22 -0
- package/eslint.config.js +6 -0
- package/package.json +72 -0
- package/src/document/document.test.ts +985 -0
- package/src/document/document.ts +281 -0
- package/src/document/index.ts +2 -0
- package/src/document/transformers/character.ts +279 -0
- package/src/document/transformers/html.ts +346 -0
- package/src/document/transformers/json.ts +265 -0
- package/src/document/transformers/latex.ts +19 -0
- package/src/document/transformers/markdown.ts +244 -0
- package/src/document/transformers/text.ts +134 -0
- package/src/document/transformers/token.ts +148 -0
- package/src/document/transformers/transformer.ts +5 -0
- package/src/document/types.ts +103 -0
- package/src/graph-rag/index.test.ts +235 -0
- package/src/graph-rag/index.ts +306 -0
- package/src/index.ts +6 -0
- package/src/rerank/index.test.ts +150 -0
- package/src/rerank/index.ts +154 -0
- package/src/tools/document-chunker.ts +30 -0
- package/src/tools/graph-rag.ts +117 -0
- package/src/tools/index.ts +3 -0
- package/src/tools/vector-query.ts +90 -0
- package/src/utils/default-settings.ts +33 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/vector-prompts.ts +729 -0
- package/src/utils/vector-search.ts +41 -0
- package/tsconfig.json +5 -0
- package/vitest.config.ts +11 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Document as Chunk,
|
|
3
|
+
IngestionPipeline,
|
|
4
|
+
KeywordExtractor,
|
|
5
|
+
QuestionsAnsweredExtractor,
|
|
6
|
+
SummaryExtractor,
|
|
7
|
+
TitleExtractor,
|
|
8
|
+
} from 'llamaindex';
|
|
9
|
+
|
|
10
|
+
import { CharacterTransformer, RecursiveCharacterTransformer } from './transformers/character';
|
|
11
|
+
import { HTMLHeaderTransformer, HTMLSectionTransformer } from './transformers/html';
|
|
12
|
+
import { RecursiveJsonTransformer } from './transformers/json';
|
|
13
|
+
import { LatexTransformer } from './transformers/latex';
|
|
14
|
+
import { MarkdownHeaderTransformer, MarkdownTransformer } from './transformers/markdown';
|
|
15
|
+
import { TokenTransformer } from './transformers/token';
|
|
16
|
+
import type { ChunkOptions, ChunkParams, ChunkStrategy, ExtractParams } from './types';
|
|
17
|
+
|
|
18
|
+
export class MDocument {
|
|
19
|
+
private chunks: Chunk[];
|
|
20
|
+
private type: string; // e.g., 'text', 'html', 'markdown', 'json'
|
|
21
|
+
|
|
22
|
+
constructor({ docs, type }: { docs: { text: string; metadata?: Record<string, any> }[]; type: string }) {
|
|
23
|
+
this.chunks = docs.map(d => {
|
|
24
|
+
return new Chunk({ text: d.text, metadata: d.metadata });
|
|
25
|
+
});
|
|
26
|
+
this.type = type;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async extractMetadata({ title, summary, questions, keywords }: ExtractParams): Promise<MDocument> {
|
|
30
|
+
const transformations = [];
|
|
31
|
+
|
|
32
|
+
if (typeof summary !== 'undefined') {
|
|
33
|
+
transformations.push(new SummaryExtractor(typeof summary === 'boolean' ? {} : summary));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (typeof questions !== 'undefined') {
|
|
37
|
+
transformations.push(new QuestionsAnsweredExtractor(typeof questions === 'boolean' ? {} : questions));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (typeof keywords !== 'undefined') {
|
|
41
|
+
transformations.push(new KeywordExtractor(typeof keywords === 'boolean' ? {} : keywords));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (typeof title !== 'undefined') {
|
|
45
|
+
transformations.push(new TitleExtractor(typeof title === 'boolean' ? {} : title));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const pipeline = new IngestionPipeline({
|
|
49
|
+
transformations,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const nodes = await pipeline.run({
|
|
53
|
+
documents: this.chunks,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
this.chunks = this.chunks.map((doc, i) => {
|
|
57
|
+
return new Chunk({
|
|
58
|
+
text: doc.text,
|
|
59
|
+
metadata: {
|
|
60
|
+
...doc.metadata,
|
|
61
|
+
...(nodes?.[i]?.metadata || {}),
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static fromText(text: string, metadata?: Record<string, any>): MDocument {
|
|
70
|
+
return new MDocument({
|
|
71
|
+
docs: [
|
|
72
|
+
{
|
|
73
|
+
text,
|
|
74
|
+
metadata,
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
type: 'text',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static fromHTML(html: string, metadata?: Record<string, any>): MDocument {
|
|
82
|
+
return new MDocument({
|
|
83
|
+
docs: [
|
|
84
|
+
{
|
|
85
|
+
text: html,
|
|
86
|
+
metadata,
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
type: 'html',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static fromMarkdown(markdown: string, metadata?: Record<string, any>): MDocument {
|
|
94
|
+
return new MDocument({
|
|
95
|
+
docs: [
|
|
96
|
+
{
|
|
97
|
+
text: markdown,
|
|
98
|
+
metadata,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
type: 'markdown',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
static fromJSON(jsonString: string, metadata?: Record<string, any>): MDocument {
|
|
106
|
+
return new MDocument({
|
|
107
|
+
docs: [
|
|
108
|
+
{
|
|
109
|
+
text: jsonString,
|
|
110
|
+
metadata,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
type: 'json',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private defaultStrategy(): ChunkStrategy {
|
|
118
|
+
switch (this.type) {
|
|
119
|
+
case 'html':
|
|
120
|
+
return 'html';
|
|
121
|
+
case 'markdown':
|
|
122
|
+
return 'markdown';
|
|
123
|
+
case 'json':
|
|
124
|
+
return 'json';
|
|
125
|
+
case 'latex':
|
|
126
|
+
return 'latex';
|
|
127
|
+
default:
|
|
128
|
+
return 'recursive';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private async chunkBy(strategy: ChunkStrategy, options?: ChunkOptions): Promise<void> {
|
|
133
|
+
switch (strategy) {
|
|
134
|
+
case 'recursive':
|
|
135
|
+
await this.chunkRecursive(options);
|
|
136
|
+
break;
|
|
137
|
+
case 'character':
|
|
138
|
+
await this.chunkCharacter(options);
|
|
139
|
+
break;
|
|
140
|
+
case 'token':
|
|
141
|
+
await this.chunkToken(options);
|
|
142
|
+
break;
|
|
143
|
+
case 'markdown':
|
|
144
|
+
await this.chunkMarkdown(options);
|
|
145
|
+
break;
|
|
146
|
+
case 'html':
|
|
147
|
+
await this.chunkHTML(options);
|
|
148
|
+
break;
|
|
149
|
+
case 'json':
|
|
150
|
+
await this.chunkJSON(options);
|
|
151
|
+
break;
|
|
152
|
+
case 'latex':
|
|
153
|
+
await this.chunkLatex(options);
|
|
154
|
+
break;
|
|
155
|
+
default:
|
|
156
|
+
throw new Error(`Unknown strategy: ${strategy}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async chunkRecursive(options?: ChunkOptions): Promise<void> {
|
|
161
|
+
if (options?.language) {
|
|
162
|
+
const rt = RecursiveCharacterTransformer.fromLanguage(options.language, options);
|
|
163
|
+
const textSplit = rt.transformDocuments(this.chunks);
|
|
164
|
+
this.chunks = textSplit;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const rt = new RecursiveCharacterTransformer({
|
|
169
|
+
separators: options?.separators,
|
|
170
|
+
isSeparatorRegex: options?.isSeparatorRegex,
|
|
171
|
+
options,
|
|
172
|
+
});
|
|
173
|
+
const textSplit = rt.transformDocuments(this.chunks);
|
|
174
|
+
this.chunks = textSplit;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async chunkCharacter(options?: ChunkOptions): Promise<void> {
|
|
178
|
+
const rt = new CharacterTransformer({
|
|
179
|
+
separator: options?.separator,
|
|
180
|
+
isSeparatorRegex: options?.isSeparatorRegex,
|
|
181
|
+
options,
|
|
182
|
+
});
|
|
183
|
+
const textSplit = rt.transformDocuments(this.chunks);
|
|
184
|
+
this.chunks = textSplit;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async chunkHTML(options?: ChunkOptions): Promise<void> {
|
|
188
|
+
if (options?.headers?.length) {
|
|
189
|
+
const rt = new HTMLHeaderTransformer(options.headers, options?.returnEachLine);
|
|
190
|
+
|
|
191
|
+
const textSplit = rt.transformDocuments(this.chunks);
|
|
192
|
+
this.chunks = textSplit;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (options?.sections?.length) {
|
|
197
|
+
const rt = new HTMLSectionTransformer(options.sections);
|
|
198
|
+
|
|
199
|
+
const textSplit = rt.transformDocuments(this.chunks);
|
|
200
|
+
this.chunks = textSplit;
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
throw new Error('HTML chunking requires either headers or sections to be specified');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async chunkJSON(options?: ChunkOptions): Promise<void> {
|
|
208
|
+
if (!options?.maxSize) {
|
|
209
|
+
throw new Error('JSON chunking requires maxSize to be specified');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const rt = new RecursiveJsonTransformer({
|
|
213
|
+
maxSize: options?.maxSize,
|
|
214
|
+
minSize: options?.minSize,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const textSplit = rt.transformDocuments({
|
|
218
|
+
documents: this.chunks,
|
|
219
|
+
ensureAscii: options?.ensureAscii,
|
|
220
|
+
convertLists: options?.convertLists,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
this.chunks = textSplit;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async chunkLatex(options?: ChunkOptions): Promise<void> {
|
|
227
|
+
const rt = new LatexTransformer(options);
|
|
228
|
+
const textSplit = rt.transformDocuments(this.chunks);
|
|
229
|
+
this.chunks = textSplit;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async chunkToken(options?: ChunkOptions): Promise<void> {
|
|
233
|
+
const rt = TokenTransformer.fromTikToken({
|
|
234
|
+
options,
|
|
235
|
+
encodingName: options?.encodingName,
|
|
236
|
+
modelName: options?.modelName,
|
|
237
|
+
});
|
|
238
|
+
const textSplit = rt.transformDocuments(this.chunks);
|
|
239
|
+
this.chunks = textSplit;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async chunkMarkdown(options?: ChunkOptions): Promise<void> {
|
|
243
|
+
if (options?.headers) {
|
|
244
|
+
const rt = new MarkdownHeaderTransformer(options.headers, options?.returnEachLine, options?.stripHeaders);
|
|
245
|
+
const textSplit = rt.transformDocuments(this.chunks);
|
|
246
|
+
this.chunks = textSplit;
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const rt = new MarkdownTransformer(options);
|
|
251
|
+
const textSplit = rt.transformDocuments(this.chunks);
|
|
252
|
+
this.chunks = textSplit;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async chunk(params?: ChunkParams): Promise<Chunk[]> {
|
|
256
|
+
const { strategy: passedStrategy, extract, ...chunkOptions } = params || {};
|
|
257
|
+
// Determine the default strategy based on type if not specified
|
|
258
|
+
const strategy = passedStrategy || this.defaultStrategy();
|
|
259
|
+
|
|
260
|
+
// Apply the appropriate chunking strategy
|
|
261
|
+
await this.chunkBy(strategy, chunkOptions);
|
|
262
|
+
|
|
263
|
+
if (extract) {
|
|
264
|
+
await this.extractMetadata(extract);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return this.chunks;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
getDocs(): Chunk[] {
|
|
271
|
+
return this.chunks;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
getText(): string[] {
|
|
275
|
+
return this.chunks.map(doc => doc.text);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
getMetadata(): Record<string, any>[] {
|
|
279
|
+
return this.chunks.map(doc => doc.metadata);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { Language } from '../types';
|
|
2
|
+
import type { ChunkOptions } from '../types';
|
|
3
|
+
|
|
4
|
+
import { TextTransformer } from './text';
|
|
5
|
+
|
|
6
|
+
function splitTextWithRegex(text: string, separator: string, keepSeparator: boolean | 'start' | 'end'): string[] {
|
|
7
|
+
if (!separator) {
|
|
8
|
+
return text.split('');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (!keepSeparator) {
|
|
12
|
+
return text.split(new RegExp(separator)).filter(s => s !== '');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!text) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Split with capturing group to keep separators
|
|
20
|
+
const splits = text.split(new RegExp(`(${separator})`));
|
|
21
|
+
const result: string[] = [];
|
|
22
|
+
|
|
23
|
+
if (keepSeparator === 'end') {
|
|
24
|
+
// Process all complete pairs
|
|
25
|
+
for (let i = 0; i < splits.length - 1; i += 2) {
|
|
26
|
+
if (i + 1 < splits.length) {
|
|
27
|
+
// Current text + separator
|
|
28
|
+
const chunk = splits[i] + (splits[i + 1] || '');
|
|
29
|
+
if (chunk) result.push(chunk);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Handle the last element if it exists and isn't a separator
|
|
33
|
+
if (splits.length % 2 === 1 && splits[splits.length - 1]) {
|
|
34
|
+
result.push(splits?.[splits.length - 1]!);
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
if (splits[0]) result.push(splits[0]);
|
|
38
|
+
|
|
39
|
+
for (let i = 1; i < splits.length - 1; i += 2) {
|
|
40
|
+
const separator = splits[i];
|
|
41
|
+
const text = splits[i + 1];
|
|
42
|
+
if (separator && text) {
|
|
43
|
+
result.push(separator + text);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return result.filter(s => s !== '');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class CharacterTransformer extends TextTransformer {
|
|
52
|
+
protected separator: string;
|
|
53
|
+
protected isSeparatorRegex: boolean;
|
|
54
|
+
|
|
55
|
+
constructor({
|
|
56
|
+
separator = '\n\n',
|
|
57
|
+
isSeparatorRegex = false,
|
|
58
|
+
options = {},
|
|
59
|
+
}: {
|
|
60
|
+
separator?: string;
|
|
61
|
+
isSeparatorRegex?: boolean;
|
|
62
|
+
options?: {
|
|
63
|
+
size?: number;
|
|
64
|
+
overlap?: number;
|
|
65
|
+
lengthFunction?: (text: string) => number;
|
|
66
|
+
keepSeparator?: boolean | 'start' | 'end';
|
|
67
|
+
addStartIndex?: boolean;
|
|
68
|
+
stripWhitespace?: boolean;
|
|
69
|
+
};
|
|
70
|
+
}) {
|
|
71
|
+
super(options);
|
|
72
|
+
this.separator = separator;
|
|
73
|
+
this.isSeparatorRegex = isSeparatorRegex;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
splitText({ text }: { text: string }): string[] {
|
|
77
|
+
// First, split the text into initial chunks
|
|
78
|
+
const separator = this.isSeparatorRegex ? this.separator : this.separator.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
79
|
+
|
|
80
|
+
const initialSplits = splitTextWithRegex(text, separator, this.keepSeparator);
|
|
81
|
+
|
|
82
|
+
// If length of any split is greater than chunk size, perform additional splitting
|
|
83
|
+
const chunks: string[] = [];
|
|
84
|
+
for (const split of initialSplits) {
|
|
85
|
+
if (this.lengthFunction(split) <= this.size) {
|
|
86
|
+
chunks.push(split);
|
|
87
|
+
} else {
|
|
88
|
+
// If a single split is too large, split it further
|
|
89
|
+
const subChunks = this.__splitChunk(split);
|
|
90
|
+
chunks.push(...subChunks);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return chunks;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private __splitChunk(text: string): string[] {
|
|
98
|
+
const chunks: string[] = [];
|
|
99
|
+
let currentChunk = '';
|
|
100
|
+
|
|
101
|
+
// Split by characters if no other separator is suitable
|
|
102
|
+
const chars = text.split('');
|
|
103
|
+
|
|
104
|
+
for (const char of chars) {
|
|
105
|
+
if (this.lengthFunction(currentChunk + char) <= this.size) {
|
|
106
|
+
currentChunk += char;
|
|
107
|
+
} else {
|
|
108
|
+
if (currentChunk) {
|
|
109
|
+
chunks.push(currentChunk);
|
|
110
|
+
}
|
|
111
|
+
currentChunk = char;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (currentChunk) {
|
|
116
|
+
chunks.push(currentChunk);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return chunks;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export class RecursiveCharacterTransformer extends TextTransformer {
|
|
124
|
+
protected separators: string[];
|
|
125
|
+
protected isSeparatorRegex: boolean;
|
|
126
|
+
|
|
127
|
+
constructor({
|
|
128
|
+
separators,
|
|
129
|
+
isSeparatorRegex = false,
|
|
130
|
+
options = {},
|
|
131
|
+
}: {
|
|
132
|
+
separators?: string[];
|
|
133
|
+
isSeparatorRegex?: boolean;
|
|
134
|
+
options?: ChunkOptions;
|
|
135
|
+
}) {
|
|
136
|
+
super(options);
|
|
137
|
+
this.separators = separators || ['\n\n', '\n', ' ', ''];
|
|
138
|
+
this.isSeparatorRegex = isSeparatorRegex;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private _splitText(text: string, separators: string[]): string[] {
|
|
142
|
+
const finalChunks: string[] = [];
|
|
143
|
+
|
|
144
|
+
let separator = separators?.[separators.length - 1]!;
|
|
145
|
+
let newSeparators: string[] = [];
|
|
146
|
+
|
|
147
|
+
for (let i = 0; i < separators.length; i++) {
|
|
148
|
+
const s = separators[i]!;
|
|
149
|
+
const _separator = this.isSeparatorRegex ? s : s?.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
150
|
+
|
|
151
|
+
if (s === '') {
|
|
152
|
+
separator = s;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (new RegExp(_separator).test(text)) {
|
|
157
|
+
separator = s;
|
|
158
|
+
newSeparators = separators.slice(i + 1);
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const _separator = this.isSeparatorRegex ? separator : separator?.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
164
|
+
|
|
165
|
+
const splits = splitTextWithRegex(text, _separator, this.keepSeparator);
|
|
166
|
+
|
|
167
|
+
const goodSplits: string[] = [];
|
|
168
|
+
const mergeSeparator = this.keepSeparator ? '' : separator;
|
|
169
|
+
|
|
170
|
+
for (const s of splits) {
|
|
171
|
+
if (this.lengthFunction(s) < this.size) {
|
|
172
|
+
goodSplits.push(s);
|
|
173
|
+
} else {
|
|
174
|
+
if (goodSplits.length > 0) {
|
|
175
|
+
const mergedText = this.mergeSplits(goodSplits, mergeSeparator);
|
|
176
|
+
finalChunks.push(...mergedText);
|
|
177
|
+
goodSplits.length = 0;
|
|
178
|
+
}
|
|
179
|
+
if (newSeparators.length === 0) {
|
|
180
|
+
finalChunks.push(s);
|
|
181
|
+
} else {
|
|
182
|
+
const otherInfo = this._splitText(s, newSeparators);
|
|
183
|
+
finalChunks.push(...otherInfo);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (goodSplits.length > 0) {
|
|
189
|
+
const mergedText = this.mergeSplits(goodSplits, mergeSeparator);
|
|
190
|
+
finalChunks.push(...mergedText);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return finalChunks;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
splitText({ text }: { text: string }): string[] {
|
|
197
|
+
return this._splitText(text, this.separators);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
static fromLanguage(
|
|
201
|
+
language: Language,
|
|
202
|
+
options: {
|
|
203
|
+
size?: number;
|
|
204
|
+
chunkOverlap?: number;
|
|
205
|
+
lengthFunction?: (text: string) => number;
|
|
206
|
+
keepSeparator?: boolean | 'start' | 'end';
|
|
207
|
+
addStartIndex?: boolean;
|
|
208
|
+
stripWhitespace?: boolean;
|
|
209
|
+
} = {},
|
|
210
|
+
): RecursiveCharacterTransformer {
|
|
211
|
+
const separators = RecursiveCharacterTransformer.getSeparatorsForLanguage(language);
|
|
212
|
+
return new RecursiveCharacterTransformer({ separators, isSeparatorRegex: true, options });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
static getSeparatorsForLanguage(language: Language): string[] {
|
|
216
|
+
switch (language) {
|
|
217
|
+
case Language.MARKDOWN:
|
|
218
|
+
return [
|
|
219
|
+
// First, try to split along Markdown headings (starting with level 2)
|
|
220
|
+
'\n#{1,6} ',
|
|
221
|
+
// End of code block
|
|
222
|
+
'```\n',
|
|
223
|
+
// Horizontal lines
|
|
224
|
+
'\n\\*\\*\\*+\n',
|
|
225
|
+
'\n---+\n',
|
|
226
|
+
'\n___+\n',
|
|
227
|
+
// Note that this splitter doesn't handle horizontal lines defined
|
|
228
|
+
// by *three or more* of ***, ---, or ___, but this is not handled
|
|
229
|
+
'\n\n',
|
|
230
|
+
'\n',
|
|
231
|
+
' ',
|
|
232
|
+
'',
|
|
233
|
+
];
|
|
234
|
+
case Language.CPP:
|
|
235
|
+
case Language.C:
|
|
236
|
+
return [
|
|
237
|
+
'\nclass ',
|
|
238
|
+
'\nvoid ',
|
|
239
|
+
'\nint ',
|
|
240
|
+
'\nfloat ',
|
|
241
|
+
'\ndouble ',
|
|
242
|
+
'\nif ',
|
|
243
|
+
'\nfor ',
|
|
244
|
+
'\nwhile ',
|
|
245
|
+
'\nswitch ',
|
|
246
|
+
'\ncase ',
|
|
247
|
+
'\n\n',
|
|
248
|
+
'\n',
|
|
249
|
+
' ',
|
|
250
|
+
'',
|
|
251
|
+
];
|
|
252
|
+
case Language.TS:
|
|
253
|
+
return [
|
|
254
|
+
'\nenum ',
|
|
255
|
+
'\ninterface ',
|
|
256
|
+
'\nnamespace ',
|
|
257
|
+
'\ntype ',
|
|
258
|
+
'\nclass ',
|
|
259
|
+
'\nfunction ',
|
|
260
|
+
'\nconst ',
|
|
261
|
+
'\nlet ',
|
|
262
|
+
'\nvar ',
|
|
263
|
+
'\nif ',
|
|
264
|
+
'\nfor ',
|
|
265
|
+
'\nwhile ',
|
|
266
|
+
'\nswitch ',
|
|
267
|
+
'\ncase ',
|
|
268
|
+
'\ndefault ',
|
|
269
|
+
'\n\n',
|
|
270
|
+
'\n',
|
|
271
|
+
' ',
|
|
272
|
+
'',
|
|
273
|
+
];
|
|
274
|
+
// ... (add other language cases following the same pattern)
|
|
275
|
+
default:
|
|
276
|
+
throw new Error(`Language ${language} is not supported! Please choose from ${Object.values(Language)}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|