@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.
Files changed (40) hide show
  1. package/.turbo/turbo-build.log +23 -0
  2. package/CHANGELOG.md +1151 -0
  3. package/LICENSE +44 -0
  4. package/README.md +26 -0
  5. package/dist/_tsup-dts-rollup.d.cts +620 -0
  6. package/dist/_tsup-dts-rollup.d.ts +620 -0
  7. package/dist/index.cjs +2524 -0
  8. package/dist/index.d.cts +22 -0
  9. package/dist/index.d.ts +22 -0
  10. package/dist/index.js +2505 -0
  11. package/docker-compose.yaml +22 -0
  12. package/eslint.config.js +6 -0
  13. package/package.json +72 -0
  14. package/src/document/document.test.ts +985 -0
  15. package/src/document/document.ts +281 -0
  16. package/src/document/index.ts +2 -0
  17. package/src/document/transformers/character.ts +279 -0
  18. package/src/document/transformers/html.ts +346 -0
  19. package/src/document/transformers/json.ts +265 -0
  20. package/src/document/transformers/latex.ts +19 -0
  21. package/src/document/transformers/markdown.ts +244 -0
  22. package/src/document/transformers/text.ts +134 -0
  23. package/src/document/transformers/token.ts +148 -0
  24. package/src/document/transformers/transformer.ts +5 -0
  25. package/src/document/types.ts +103 -0
  26. package/src/graph-rag/index.test.ts +235 -0
  27. package/src/graph-rag/index.ts +306 -0
  28. package/src/index.ts +6 -0
  29. package/src/rerank/index.test.ts +150 -0
  30. package/src/rerank/index.ts +154 -0
  31. package/src/tools/document-chunker.ts +30 -0
  32. package/src/tools/graph-rag.ts +117 -0
  33. package/src/tools/index.ts +3 -0
  34. package/src/tools/vector-query.ts +90 -0
  35. package/src/utils/default-settings.ts +33 -0
  36. package/src/utils/index.ts +2 -0
  37. package/src/utils/vector-prompts.ts +729 -0
  38. package/src/utils/vector-search.ts +41 -0
  39. package/tsconfig.json +5 -0
  40. package/vitest.config.ts +11 -0
package/dist/index.js ADDED
@@ -0,0 +1,2505 @@
1
+ import { Document, SummaryExtractor, QuestionsAnsweredExtractor, KeywordExtractor, TitleExtractor, IngestionPipeline } from 'llamaindex';
2
+ import { parse } from 'node-html-better-parser';
3
+ import { encodingForModel, getEncoding } from 'js-tiktoken';
4
+ import { CohereRelevanceScorer, MastraAgentRelevanceScorer } from '@mastra/core/relevance';
5
+ import { createTool } from '@mastra/core/tools';
6
+ import { z } from 'zod';
7
+ import { embed } from 'ai';
8
+
9
+ // src/document/document.ts
10
+
11
+ // src/document/types.ts
12
+ var Language = /* @__PURE__ */ ((Language2) => {
13
+ Language2["CPP"] = "cpp";
14
+ Language2["GO"] = "go";
15
+ Language2["JAVA"] = "java";
16
+ Language2["KOTLIN"] = "kotlin";
17
+ Language2["JS"] = "js";
18
+ Language2["TS"] = "ts";
19
+ Language2["PHP"] = "php";
20
+ Language2["PROTO"] = "proto";
21
+ Language2["PYTHON"] = "python";
22
+ Language2["RST"] = "rst";
23
+ Language2["RUBY"] = "ruby";
24
+ Language2["RUST"] = "rust";
25
+ Language2["SCALA"] = "scala";
26
+ Language2["SWIFT"] = "swift";
27
+ Language2["MARKDOWN"] = "markdown";
28
+ Language2["LATEX"] = "latex";
29
+ Language2["HTML"] = "html";
30
+ Language2["SOL"] = "sol";
31
+ Language2["CSHARP"] = "csharp";
32
+ Language2["COBOL"] = "cobol";
33
+ Language2["C"] = "c";
34
+ Language2["LUA"] = "lua";
35
+ Language2["PERL"] = "perl";
36
+ Language2["HASKELL"] = "haskell";
37
+ Language2["ELIXIR"] = "elixir";
38
+ Language2["POWERSHELL"] = "powershell";
39
+ return Language2;
40
+ })(Language || {});
41
+ var TextTransformer = class {
42
+ size;
43
+ overlap;
44
+ lengthFunction;
45
+ keepSeparator;
46
+ addStartIndex;
47
+ stripWhitespace;
48
+ constructor({
49
+ size = 4e3,
50
+ overlap = 200,
51
+ lengthFunction = (text) => text.length,
52
+ keepSeparator = false,
53
+ addStartIndex = false,
54
+ stripWhitespace = true
55
+ }) {
56
+ if (overlap > size) {
57
+ throw new Error(`Got a larger chunk overlap (${overlap}) than chunk size (${size}), should be smaller.`);
58
+ }
59
+ this.size = size;
60
+ this.overlap = overlap;
61
+ this.lengthFunction = lengthFunction;
62
+ this.keepSeparator = keepSeparator;
63
+ this.addStartIndex = addStartIndex;
64
+ this.stripWhitespace = stripWhitespace;
65
+ }
66
+ setAddStartIndex(value) {
67
+ this.addStartIndex = value;
68
+ }
69
+ createDocuments(texts, metadatas) {
70
+ const _metadatas = metadatas || Array(texts.length).fill({});
71
+ const documents = [];
72
+ texts.forEach((text, i) => {
73
+ let index = 0;
74
+ let previousChunkLen = 0;
75
+ this.splitText({ text }).forEach((chunk) => {
76
+ const metadata = { ..._metadatas[i] };
77
+ if (this.addStartIndex) {
78
+ const offset = index + previousChunkLen - this.overlap;
79
+ index = text.indexOf(chunk, Math.max(0, offset));
80
+ metadata.startIndex = index;
81
+ previousChunkLen = chunk.length;
82
+ }
83
+ documents.push(
84
+ new Document({
85
+ text: chunk,
86
+ metadata
87
+ })
88
+ );
89
+ });
90
+ });
91
+ return documents;
92
+ }
93
+ splitDocuments(documents) {
94
+ const texts = [];
95
+ const metadatas = [];
96
+ for (const doc of documents) {
97
+ texts.push(doc.text);
98
+ metadatas.push(doc.metadata);
99
+ }
100
+ return this.createDocuments(texts, metadatas);
101
+ }
102
+ transformDocuments(documents) {
103
+ const texts = [];
104
+ const metadatas = [];
105
+ for (const doc of documents) {
106
+ texts.push(doc.text);
107
+ metadatas.push(doc.metadata);
108
+ }
109
+ return this.createDocuments(texts, metadatas);
110
+ }
111
+ joinDocs(docs, separator) {
112
+ let text = docs.join(separator);
113
+ if (this.stripWhitespace) {
114
+ text = text.trim();
115
+ }
116
+ return text === "" ? null : text;
117
+ }
118
+ mergeSplits(splits, separator) {
119
+ const separatorLen = this.lengthFunction(separator);
120
+ const docs = [];
121
+ let currentDoc = [];
122
+ let total = 0;
123
+ splits.forEach((d) => {
124
+ const len = this.lengthFunction(d);
125
+ if (total + len + (currentDoc.length > 0 ? separatorLen : 0) > this.size) {
126
+ if (total > this.size) {
127
+ console.warn(`Created a chunk of size ${total}, which is longer than the specified ${this.size}`);
128
+ }
129
+ if (currentDoc.length > 0) {
130
+ const doc2 = this.joinDocs(currentDoc, separator);
131
+ if (doc2 !== null) {
132
+ docs.push(doc2);
133
+ }
134
+ while (total > this.overlap || total + len + (currentDoc.length > 0 ? separatorLen : 0) > this.size && total > 0) {
135
+ total -= this.lengthFunction(currentDoc?.[0]) + (currentDoc.length > 1 ? separatorLen : 0);
136
+ currentDoc = currentDoc.slice(1);
137
+ }
138
+ }
139
+ }
140
+ currentDoc.push(d);
141
+ total += len + (currentDoc.length > 1 ? separatorLen : 0);
142
+ });
143
+ const doc = this.joinDocs(currentDoc, separator);
144
+ if (doc !== null) {
145
+ docs.push(doc);
146
+ }
147
+ return docs;
148
+ }
149
+ };
150
+
151
+ // src/document/transformers/character.ts
152
+ function splitTextWithRegex(text, separator, keepSeparator) {
153
+ if (!separator) {
154
+ return text.split("");
155
+ }
156
+ if (!keepSeparator) {
157
+ return text.split(new RegExp(separator)).filter((s) => s !== "");
158
+ }
159
+ if (!text) {
160
+ return [];
161
+ }
162
+ const splits = text.split(new RegExp(`(${separator})`));
163
+ const result = [];
164
+ if (keepSeparator === "end") {
165
+ for (let i = 0; i < splits.length - 1; i += 2) {
166
+ if (i + 1 < splits.length) {
167
+ const chunk = splits[i] + (splits[i + 1] || "");
168
+ if (chunk) result.push(chunk);
169
+ }
170
+ }
171
+ if (splits.length % 2 === 1 && splits[splits.length - 1]) {
172
+ result.push(splits?.[splits.length - 1]);
173
+ }
174
+ } else {
175
+ if (splits[0]) result.push(splits[0]);
176
+ for (let i = 1; i < splits.length - 1; i += 2) {
177
+ const separator2 = splits[i];
178
+ const text2 = splits[i + 1];
179
+ if (separator2 && text2) {
180
+ result.push(separator2 + text2);
181
+ }
182
+ }
183
+ }
184
+ return result.filter((s) => s !== "");
185
+ }
186
+ var CharacterTransformer = class extends TextTransformer {
187
+ separator;
188
+ isSeparatorRegex;
189
+ constructor({
190
+ separator = "\n\n",
191
+ isSeparatorRegex = false,
192
+ options = {}
193
+ }) {
194
+ super(options);
195
+ this.separator = separator;
196
+ this.isSeparatorRegex = isSeparatorRegex;
197
+ }
198
+ splitText({ text }) {
199
+ const separator = this.isSeparatorRegex ? this.separator : this.separator.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
200
+ const initialSplits = splitTextWithRegex(text, separator, this.keepSeparator);
201
+ const chunks = [];
202
+ for (const split of initialSplits) {
203
+ if (this.lengthFunction(split) <= this.size) {
204
+ chunks.push(split);
205
+ } else {
206
+ const subChunks = this.__splitChunk(split);
207
+ chunks.push(...subChunks);
208
+ }
209
+ }
210
+ return chunks;
211
+ }
212
+ __splitChunk(text) {
213
+ const chunks = [];
214
+ let currentChunk = "";
215
+ const chars = text.split("");
216
+ for (const char of chars) {
217
+ if (this.lengthFunction(currentChunk + char) <= this.size) {
218
+ currentChunk += char;
219
+ } else {
220
+ if (currentChunk) {
221
+ chunks.push(currentChunk);
222
+ }
223
+ currentChunk = char;
224
+ }
225
+ }
226
+ if (currentChunk) {
227
+ chunks.push(currentChunk);
228
+ }
229
+ return chunks;
230
+ }
231
+ };
232
+ var RecursiveCharacterTransformer = class _RecursiveCharacterTransformer extends TextTransformer {
233
+ separators;
234
+ isSeparatorRegex;
235
+ constructor({
236
+ separators,
237
+ isSeparatorRegex = false,
238
+ options = {}
239
+ }) {
240
+ super(options);
241
+ this.separators = separators || ["\n\n", "\n", " ", ""];
242
+ this.isSeparatorRegex = isSeparatorRegex;
243
+ }
244
+ _splitText(text, separators) {
245
+ const finalChunks = [];
246
+ let separator = separators?.[separators.length - 1];
247
+ let newSeparators = [];
248
+ for (let i = 0; i < separators.length; i++) {
249
+ const s = separators[i];
250
+ const _separator2 = this.isSeparatorRegex ? s : s?.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
251
+ if (s === "") {
252
+ separator = s;
253
+ break;
254
+ }
255
+ if (new RegExp(_separator2).test(text)) {
256
+ separator = s;
257
+ newSeparators = separators.slice(i + 1);
258
+ break;
259
+ }
260
+ }
261
+ const _separator = this.isSeparatorRegex ? separator : separator?.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
262
+ const splits = splitTextWithRegex(text, _separator, this.keepSeparator);
263
+ const goodSplits = [];
264
+ const mergeSeparator = this.keepSeparator ? "" : separator;
265
+ for (const s of splits) {
266
+ if (this.lengthFunction(s) < this.size) {
267
+ goodSplits.push(s);
268
+ } else {
269
+ if (goodSplits.length > 0) {
270
+ const mergedText = this.mergeSplits(goodSplits, mergeSeparator);
271
+ finalChunks.push(...mergedText);
272
+ goodSplits.length = 0;
273
+ }
274
+ if (newSeparators.length === 0) {
275
+ finalChunks.push(s);
276
+ } else {
277
+ const otherInfo = this._splitText(s, newSeparators);
278
+ finalChunks.push(...otherInfo);
279
+ }
280
+ }
281
+ }
282
+ if (goodSplits.length > 0) {
283
+ const mergedText = this.mergeSplits(goodSplits, mergeSeparator);
284
+ finalChunks.push(...mergedText);
285
+ }
286
+ return finalChunks;
287
+ }
288
+ splitText({ text }) {
289
+ return this._splitText(text, this.separators);
290
+ }
291
+ static fromLanguage(language, options = {}) {
292
+ const separators = _RecursiveCharacterTransformer.getSeparatorsForLanguage(language);
293
+ return new _RecursiveCharacterTransformer({ separators, isSeparatorRegex: true, options });
294
+ }
295
+ static getSeparatorsForLanguage(language) {
296
+ switch (language) {
297
+ case "markdown" /* MARKDOWN */:
298
+ return [
299
+ // First, try to split along Markdown headings (starting with level 2)
300
+ "\n#{1,6} ",
301
+ // End of code block
302
+ "```\n",
303
+ // Horizontal lines
304
+ "\n\\*\\*\\*+\n",
305
+ "\n---+\n",
306
+ "\n___+\n",
307
+ // Note that this splitter doesn't handle horizontal lines defined
308
+ // by *three or more* of ***, ---, or ___, but this is not handled
309
+ "\n\n",
310
+ "\n",
311
+ " ",
312
+ ""
313
+ ];
314
+ case "cpp" /* CPP */:
315
+ case "c" /* C */:
316
+ return [
317
+ "\nclass ",
318
+ "\nvoid ",
319
+ "\nint ",
320
+ "\nfloat ",
321
+ "\ndouble ",
322
+ "\nif ",
323
+ "\nfor ",
324
+ "\nwhile ",
325
+ "\nswitch ",
326
+ "\ncase ",
327
+ "\n\n",
328
+ "\n",
329
+ " ",
330
+ ""
331
+ ];
332
+ case "ts" /* TS */:
333
+ return [
334
+ "\nenum ",
335
+ "\ninterface ",
336
+ "\nnamespace ",
337
+ "\ntype ",
338
+ "\nclass ",
339
+ "\nfunction ",
340
+ "\nconst ",
341
+ "\nlet ",
342
+ "\nvar ",
343
+ "\nif ",
344
+ "\nfor ",
345
+ "\nwhile ",
346
+ "\nswitch ",
347
+ "\ncase ",
348
+ "\ndefault ",
349
+ "\n\n",
350
+ "\n",
351
+ " ",
352
+ ""
353
+ ];
354
+ // ... (add other language cases following the same pattern)
355
+ default:
356
+ throw new Error(`Language ${language} is not supported! Please choose from ${Object.values(Language)}`);
357
+ }
358
+ }
359
+ };
360
+ var HTMLHeaderTransformer = class {
361
+ headersToSplitOn;
362
+ returnEachElement;
363
+ constructor(headersToSplitOn, returnEachElement = false) {
364
+ this.returnEachElement = returnEachElement;
365
+ this.headersToSplitOn = [...headersToSplitOn].sort();
366
+ }
367
+ splitText({ text }) {
368
+ const root = parse(text);
369
+ const headerFilter = this.headersToSplitOn.map(([header]) => header);
370
+ const headerMapping = Object.fromEntries(this.headersToSplitOn);
371
+ const elements = [];
372
+ const headers = root.querySelectorAll(headerFilter.join(","));
373
+ headers.forEach((header) => {
374
+ let content = "";
375
+ const parentNode = header.parentNode;
376
+ if (parentNode && parentNode.childNodes) {
377
+ let foundHeader = false;
378
+ for (const node of parentNode.childNodes) {
379
+ if (node === header) {
380
+ foundHeader = true;
381
+ continue;
382
+ }
383
+ if (foundHeader && node.tagName && headerFilter.includes(node.tagName.toLowerCase())) {
384
+ break;
385
+ }
386
+ if (foundHeader) {
387
+ content += this.getTextContent(node) + " ";
388
+ }
389
+ }
390
+ }
391
+ elements.push({
392
+ url: text,
393
+ xpath: this.getXPath(header),
394
+ content: content.trim(),
395
+ metadata: {
396
+ [headerMapping?.[header.tagName.toLowerCase()]]: header.text || ""
397
+ }
398
+ });
399
+ });
400
+ return this.returnEachElement ? elements.map(
401
+ (el) => new Document({
402
+ text: el.content,
403
+ metadata: { ...el.metadata, xpath: el.xpath }
404
+ })
405
+ ) : this.aggregateElementsToChunks(elements);
406
+ }
407
+ getXPath(element) {
408
+ if (!element) return "";
409
+ const parts = [];
410
+ let current = element;
411
+ while (current && current.tagName) {
412
+ let index = 1;
413
+ const parent = current.parentNode;
414
+ if (parent && parent.childNodes) {
415
+ for (const sibling of parent.childNodes) {
416
+ if (sibling === current) break;
417
+ if (sibling.tagName === current.tagName) {
418
+ index++;
419
+ }
420
+ }
421
+ }
422
+ parts.unshift(`${current.tagName.toLowerCase()}[${index}]`);
423
+ current = current.parentNode;
424
+ }
425
+ return "/" + parts.join("/");
426
+ }
427
+ getTextContent(element) {
428
+ if (!element) return "";
429
+ if (!element.tagName) {
430
+ return element.text || "";
431
+ }
432
+ let content = element.text || "";
433
+ if (element.childNodes) {
434
+ for (const child of element.childNodes) {
435
+ const childText = this.getTextContent(child);
436
+ if (childText) {
437
+ content += " " + childText;
438
+ }
439
+ }
440
+ }
441
+ return content.trim();
442
+ }
443
+ aggregateElementsToChunks(elements) {
444
+ const aggregatedChunks = [];
445
+ for (const element of elements) {
446
+ if (aggregatedChunks.length > 0 && JSON.stringify(aggregatedChunks[aggregatedChunks.length - 1].metadata) === JSON.stringify(element.metadata)) {
447
+ aggregatedChunks[aggregatedChunks.length - 1].content += " \n" + element.content;
448
+ } else {
449
+ aggregatedChunks.push({ ...element });
450
+ }
451
+ }
452
+ return aggregatedChunks.map(
453
+ (chunk) => new Document({
454
+ text: chunk.content,
455
+ metadata: { ...chunk.metadata, xpath: chunk.xpath }
456
+ })
457
+ );
458
+ }
459
+ createDocuments(texts, metadatas) {
460
+ const _metadatas = metadatas || Array(texts.length).fill({});
461
+ const documents = [];
462
+ for (let i = 0; i < texts.length; i++) {
463
+ const chunks = this.splitText({ text: texts[i] });
464
+ for (const chunk of chunks) {
465
+ const metadata = { ..._metadatas[i] || {} };
466
+ const chunkMetadata = chunk.metadata;
467
+ if (chunkMetadata) {
468
+ for (const [key, value] of Object.entries(chunkMetadata || {})) {
469
+ if (value === "#TITLE#") {
470
+ chunkMetadata[key] = metadata["Title"];
471
+ }
472
+ }
473
+ }
474
+ documents.push(
475
+ new Document({
476
+ text: chunk.text,
477
+ metadata: { ...metadata, ...chunkMetadata }
478
+ })
479
+ );
480
+ }
481
+ }
482
+ return documents;
483
+ }
484
+ transformDocuments(documents) {
485
+ const texts = [];
486
+ const metadatas = [];
487
+ for (const doc of documents) {
488
+ texts.push(doc.text);
489
+ metadatas.push(doc.metadata);
490
+ }
491
+ return this.createDocuments(texts, metadatas);
492
+ }
493
+ };
494
+ var HTMLSectionTransformer = class {
495
+ headersToSplitOn;
496
+ options;
497
+ constructor(headersToSplitOn, options = {}) {
498
+ this.headersToSplitOn = Object.fromEntries(headersToSplitOn.map(([tag, name]) => [tag.toLowerCase(), name]));
499
+ this.options = options;
500
+ }
501
+ splitText(text) {
502
+ const sections = this.splitHtmlByHeaders(text);
503
+ return sections.map(
504
+ (section) => new Document({
505
+ text: section.content,
506
+ metadata: {
507
+ [this.headersToSplitOn[section.tagName.toLowerCase()]]: section.header,
508
+ xpath: section.xpath
509
+ }
510
+ })
511
+ );
512
+ }
513
+ getXPath(element) {
514
+ const parts = [];
515
+ let current = element;
516
+ while (current && current.nodeType === 1) {
517
+ let index = 1;
518
+ let sibling = current.previousSibling;
519
+ while (sibling) {
520
+ if (sibling.nodeType === 1 && sibling.tagName === current.tagName) {
521
+ index++;
522
+ }
523
+ sibling = sibling.previousSibling;
524
+ }
525
+ if (current.tagName) {
526
+ parts.unshift(`${current.tagName.toLowerCase()}[${index}]`);
527
+ }
528
+ current = current.parentNode;
529
+ }
530
+ return "/" + parts.join("/");
531
+ }
532
+ splitHtmlByHeaders(htmlDoc) {
533
+ const sections = [];
534
+ const root = parse(htmlDoc);
535
+ const headers = Object.keys(this.headersToSplitOn);
536
+ const headerElements = root.querySelectorAll(headers.join(","));
537
+ headerElements.forEach((headerElement, index) => {
538
+ const header = headerElement.text?.trim() || "";
539
+ const tagName = headerElement.tagName;
540
+ const xpath = this.getXPath(headerElement);
541
+ let content = "";
542
+ let currentElement = headerElement.nextElementSibling;
543
+ const nextHeader = headerElements[index + 1];
544
+ while (currentElement && (!nextHeader || currentElement !== nextHeader)) {
545
+ if (currentElement.text) {
546
+ content += currentElement.text.trim() + " ";
547
+ }
548
+ currentElement = currentElement.nextElementSibling;
549
+ }
550
+ content = content.trim();
551
+ sections.push({
552
+ header,
553
+ content,
554
+ tagName,
555
+ xpath
556
+ });
557
+ });
558
+ return sections;
559
+ }
560
+ async splitDocuments(documents) {
561
+ const texts = [];
562
+ const metadatas = [];
563
+ for (const doc of documents) {
564
+ texts.push(doc.text);
565
+ metadatas.push(doc.metadata);
566
+ }
567
+ const results = await this.createDocuments(texts, metadatas);
568
+ const textSplitter = new RecursiveCharacterTransformer({ options: this.options });
569
+ return textSplitter.splitDocuments(results);
570
+ }
571
+ createDocuments(texts, metadatas) {
572
+ const _metadatas = metadatas || Array(texts.length).fill({});
573
+ const documents = [];
574
+ for (let i = 0; i < texts.length; i++) {
575
+ const chunks = this.splitText(texts[i]);
576
+ for (const chunk of chunks) {
577
+ const metadata = { ..._metadatas[i] || {} };
578
+ const chunkMetadata = chunk.metadata;
579
+ if (chunkMetadata) {
580
+ for (const [key, value] of Object.entries(chunkMetadata || {})) {
581
+ if (value === "#TITLE#") {
582
+ chunkMetadata[key] = metadata["Title"];
583
+ }
584
+ }
585
+ }
586
+ documents.push(
587
+ new Document({
588
+ text: chunk.text,
589
+ metadata: { ...metadata, ...chunkMetadata }
590
+ })
591
+ );
592
+ }
593
+ }
594
+ return documents;
595
+ }
596
+ transformDocuments(documents) {
597
+ const texts = [];
598
+ const metadatas = [];
599
+ for (const doc of documents) {
600
+ texts.push(doc.text);
601
+ metadatas.push(doc.metadata);
602
+ }
603
+ return this.createDocuments(texts, metadatas);
604
+ }
605
+ };
606
+ var RecursiveJsonTransformer = class _RecursiveJsonTransformer {
607
+ maxSize;
608
+ minSize;
609
+ constructor({ maxSize = 2e3, minSize }) {
610
+ this.maxSize = maxSize;
611
+ this.minSize = minSize ?? Math.max(maxSize - 200, 50);
612
+ }
613
+ static jsonSize(data) {
614
+ const seen = /* @__PURE__ */ new WeakSet();
615
+ function getStringifiableData(obj) {
616
+ if (obj === null || typeof obj !== "object") {
617
+ return obj;
618
+ }
619
+ if (seen.has(obj)) {
620
+ return "[Circular]";
621
+ }
622
+ seen.add(obj);
623
+ if (Array.isArray(obj)) {
624
+ const safeArray = [];
625
+ for (const item of obj) {
626
+ safeArray.push(getStringifiableData(item));
627
+ }
628
+ return safeArray;
629
+ }
630
+ const safeObj = {};
631
+ for (const key in obj) {
632
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
633
+ safeObj[key] = getStringifiableData(obj[key]);
634
+ }
635
+ }
636
+ return safeObj;
637
+ }
638
+ const stringifiable = getStringifiableData(data);
639
+ return JSON.stringify(stringifiable).length;
640
+ }
641
+ /**
642
+ * Transform JSON data while handling circular references
643
+ */
644
+ transform(data) {
645
+ const size = _RecursiveJsonTransformer.jsonSize(data);
646
+ const seen = /* @__PURE__ */ new WeakSet();
647
+ function createSafeCopy(obj) {
648
+ if (obj === null || typeof obj !== "object") {
649
+ return obj;
650
+ }
651
+ if (seen.has(obj)) {
652
+ return "[Circular]";
653
+ }
654
+ seen.add(obj);
655
+ if (Array.isArray(obj)) {
656
+ return obj.map((item) => createSafeCopy(item));
657
+ }
658
+ const copy = {};
659
+ for (const key in obj) {
660
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
661
+ copy[key] = createSafeCopy(obj[key]);
662
+ }
663
+ }
664
+ return copy;
665
+ }
666
+ return {
667
+ size,
668
+ data: createSafeCopy(data)
669
+ };
670
+ }
671
+ /**
672
+ * Set a value in a nested dictionary based on the given path
673
+ */
674
+ static setNestedDict(d, path, value) {
675
+ let current = d;
676
+ for (const key of path.slice(0, -1)) {
677
+ current[key] = current[key] || {};
678
+ current = current[key];
679
+ }
680
+ current[path[path.length - 1]] = value;
681
+ }
682
+ /**
683
+ * Convert lists in the JSON structure to dictionaries with index-based keys
684
+ */
685
+ listToDictPreprocessing(data) {
686
+ if (data && typeof data === "object") {
687
+ if (Array.isArray(data)) {
688
+ return Object.fromEntries(data.map((item, index) => [String(index), this.listToDictPreprocessing(item)]));
689
+ }
690
+ return Object.fromEntries(Object.entries(data).map(([k, v]) => [k, this.listToDictPreprocessing(v)]));
691
+ }
692
+ return data;
693
+ }
694
+ /**
695
+ * Split json into maximum size dictionaries while preserving structure
696
+ */
697
+ jsonSplit({
698
+ data,
699
+ currentPath = [],
700
+ chunks = [{}]
701
+ }) {
702
+ if (data && typeof data === "object" && !Array.isArray(data)) {
703
+ for (const [key, value] of Object.entries(data)) {
704
+ const newPath = [...currentPath, key];
705
+ const chunkSize = _RecursiveJsonTransformer.jsonSize(chunks[chunks.length - 1] || {});
706
+ const size = _RecursiveJsonTransformer.jsonSize({ [key]: value });
707
+ const remaining = this.maxSize - chunkSize;
708
+ if (size < remaining) {
709
+ _RecursiveJsonTransformer.setNestedDict(chunks[chunks.length - 1] || {}, newPath, value);
710
+ } else {
711
+ if (chunkSize >= this.minSize) {
712
+ chunks.push({});
713
+ }
714
+ this.jsonSplit({
715
+ data: typeof value === "object" ? value : { [key]: value },
716
+ currentPath: newPath,
717
+ chunks
718
+ });
719
+ }
720
+ }
721
+ } else {
722
+ _RecursiveJsonTransformer.setNestedDict(chunks[chunks.length - 1] || {}, currentPath, data);
723
+ }
724
+ return chunks;
725
+ }
726
+ /**
727
+ * Splits JSON into a list of JSON chunks
728
+ */
729
+ splitJson({
730
+ jsonData,
731
+ convertLists = false
732
+ }) {
733
+ const processedData = convertLists ? this.listToDictPreprocessing(jsonData) : jsonData;
734
+ const chunks = this.jsonSplit({ data: processedData });
735
+ if (Object.keys(chunks[chunks.length - 1] || {}).length === 0) {
736
+ chunks.pop();
737
+ }
738
+ return chunks;
739
+ }
740
+ escapeNonAscii(obj) {
741
+ if (typeof obj === "string") {
742
+ return obj.replace(/[\u0080-\uffff]/g, (char) => {
743
+ return `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`;
744
+ });
745
+ }
746
+ if (typeof obj === "object" && obj !== null) {
747
+ return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, this.escapeNonAscii(value)]));
748
+ }
749
+ return obj;
750
+ }
751
+ /**
752
+ * Splits JSON into a list of JSON formatted strings
753
+ */
754
+ splitText({
755
+ jsonData,
756
+ convertLists = false,
757
+ ensureAscii = true
758
+ }) {
759
+ const chunks = this.splitJson({ jsonData, convertLists });
760
+ if (ensureAscii) {
761
+ const escapedChunks = chunks.map((chunk) => this.escapeNonAscii(chunk));
762
+ return escapedChunks.map((chunk) => JSON.stringify(chunk));
763
+ }
764
+ return chunks.map((chunk) => JSON.stringify(chunk));
765
+ }
766
+ /**
767
+ * Create documents from a list of json objects
768
+ */
769
+ createDocuments({
770
+ texts,
771
+ convertLists = false,
772
+ ensureAscii = true,
773
+ metadatas
774
+ }) {
775
+ const _metadatas = metadatas || Array(texts.length).fill({});
776
+ const documents = [];
777
+ texts.forEach((text, i) => {
778
+ const chunks = this.splitText({ jsonData: JSON.parse(text), convertLists, ensureAscii });
779
+ chunks.forEach((chunk) => {
780
+ const metadata = { ..._metadatas[i] || {} };
781
+ documents.push(
782
+ new Document({
783
+ text: chunk,
784
+ metadata
785
+ })
786
+ );
787
+ });
788
+ });
789
+ return documents;
790
+ }
791
+ transformDocuments({
792
+ ensureAscii,
793
+ documents,
794
+ convertLists
795
+ }) {
796
+ const texts = [];
797
+ const metadatas = [];
798
+ for (const doc of documents) {
799
+ texts.push(doc.text);
800
+ metadatas.push(doc.metadata);
801
+ }
802
+ return this.createDocuments({
803
+ texts,
804
+ metadatas,
805
+ ensureAscii,
806
+ convertLists
807
+ });
808
+ }
809
+ };
810
+
811
+ // src/document/transformers/latex.ts
812
+ var LatexTransformer = class extends RecursiveCharacterTransformer {
813
+ constructor(options = {}) {
814
+ const separators = RecursiveCharacterTransformer.getSeparatorsForLanguage("latex" /* LATEX */);
815
+ super({ separators, isSeparatorRegex: true, options });
816
+ }
817
+ };
818
+ var MarkdownTransformer = class extends RecursiveCharacterTransformer {
819
+ constructor(options = {}) {
820
+ const separators = RecursiveCharacterTransformer.getSeparatorsForLanguage("markdown" /* MARKDOWN */);
821
+ super({ separators, isSeparatorRegex: true, options });
822
+ }
823
+ };
824
+ var MarkdownHeaderTransformer = class {
825
+ headersToSplitOn;
826
+ returnEachLine;
827
+ stripHeaders;
828
+ constructor(headersToSplitOn, returnEachLine = false, stripHeaders = true) {
829
+ this.headersToSplitOn = [...headersToSplitOn].sort((a, b) => b[0].length - a[0].length);
830
+ this.returnEachLine = returnEachLine;
831
+ this.stripHeaders = stripHeaders;
832
+ }
833
+ aggregateLinesToChunks(lines) {
834
+ if (this.returnEachLine) {
835
+ return lines.flatMap((line) => {
836
+ const contentLines = line.content.split("\n");
837
+ return contentLines.filter((l) => l.trim() !== "" || this.headersToSplitOn.some(([sep]) => l.trim().startsWith(sep))).map(
838
+ (l) => new Document({
839
+ text: l.trim(),
840
+ metadata: line.metadata
841
+ })
842
+ );
843
+ });
844
+ }
845
+ const aggregatedChunks = [];
846
+ for (const line of lines) {
847
+ if (aggregatedChunks.length > 0 && JSON.stringify(aggregatedChunks?.[aggregatedChunks.length - 1].metadata) === JSON.stringify(line.metadata)) {
848
+ const aggChunk = aggregatedChunks[aggregatedChunks.length - 1];
849
+ aggChunk.content += " \n" + line.content;
850
+ } else if (aggregatedChunks.length > 0 && JSON.stringify(aggregatedChunks?.[aggregatedChunks.length - 1].metadata) !== JSON.stringify(line.metadata) && Object.keys(aggregatedChunks?.[aggregatedChunks.length - 1].metadata).length < Object.keys(line.metadata).length && aggregatedChunks?.[aggregatedChunks.length - 1]?.content?.split("\n")?.slice(-1)[0][0] === "#" && !this.stripHeaders) {
851
+ if (aggregatedChunks && aggregatedChunks?.[aggregatedChunks.length - 1]) {
852
+ const aggChunk = aggregatedChunks[aggregatedChunks.length - 1];
853
+ if (aggChunk) {
854
+ aggChunk.content += " \n" + line.content;
855
+ aggChunk.metadata = line.metadata;
856
+ }
857
+ }
858
+ } else {
859
+ aggregatedChunks.push(line);
860
+ }
861
+ }
862
+ return aggregatedChunks.map(
863
+ (chunk) => new Document({
864
+ text: chunk.content,
865
+ metadata: chunk.metadata
866
+ })
867
+ );
868
+ }
869
+ splitText({ text }) {
870
+ const lines = text.split("\n");
871
+ const linesWithMetadata = [];
872
+ let currentContent = [];
873
+ let currentMetadata = {};
874
+ const headerStack = [];
875
+ const initialMetadata = {};
876
+ let inCodeBlock = false;
877
+ let openingFence = "";
878
+ for (let i = 0; i < lines.length; i++) {
879
+ const line = lines[i];
880
+ const strippedLine = line.trim();
881
+ if (!inCodeBlock) {
882
+ if (strippedLine.startsWith("```") && strippedLine.split("```").length === 2 || strippedLine.startsWith("~~~")) {
883
+ inCodeBlock = true;
884
+ openingFence = strippedLine.startsWith("```") ? "```" : "~~~";
885
+ }
886
+ } else {
887
+ if (strippedLine.startsWith(openingFence)) {
888
+ inCodeBlock = false;
889
+ openingFence = "";
890
+ }
891
+ }
892
+ if (inCodeBlock) {
893
+ currentContent.push(line);
894
+ continue;
895
+ }
896
+ let headerMatched = false;
897
+ for (const [sep, name] of this.headersToSplitOn) {
898
+ if (strippedLine.startsWith(sep) && (strippedLine.length === sep.length || strippedLine[sep.length] === " ")) {
899
+ headerMatched = true;
900
+ if (currentContent.length > 0) {
901
+ linesWithMetadata.push({
902
+ content: currentContent.join("\n"),
903
+ metadata: { ...currentMetadata }
904
+ });
905
+ currentContent = [];
906
+ }
907
+ if (name !== null) {
908
+ const currentHeaderLevel = (sep.match(/#/g) || []).length;
909
+ while (headerStack.length > 0 && headerStack?.[headerStack.length - 1].level >= currentHeaderLevel) {
910
+ const poppedHeader = headerStack.pop();
911
+ if (poppedHeader.name in initialMetadata) {
912
+ delete initialMetadata[poppedHeader.name];
913
+ }
914
+ }
915
+ const header = {
916
+ level: currentHeaderLevel,
917
+ name,
918
+ data: strippedLine.slice(sep.length).trim()
919
+ };
920
+ headerStack.push(header);
921
+ initialMetadata[name] = header.data;
922
+ }
923
+ linesWithMetadata.push({
924
+ content: line,
925
+ metadata: { ...currentMetadata, ...initialMetadata }
926
+ });
927
+ break;
928
+ }
929
+ }
930
+ if (!headerMatched) {
931
+ if (strippedLine || this.returnEachLine) {
932
+ currentContent.push(line);
933
+ if (this.returnEachLine) {
934
+ linesWithMetadata.push({
935
+ content: line,
936
+ metadata: { ...currentMetadata }
937
+ });
938
+ currentContent = [];
939
+ }
940
+ } else if (currentContent.length > 0) {
941
+ linesWithMetadata.push({
942
+ content: currentContent.join("\n"),
943
+ metadata: { ...currentMetadata }
944
+ });
945
+ currentContent = [];
946
+ }
947
+ }
948
+ currentMetadata = { ...initialMetadata };
949
+ }
950
+ if (currentContent.length > 0) {
951
+ linesWithMetadata.push({
952
+ content: currentContent.join("\n"),
953
+ metadata: currentMetadata
954
+ });
955
+ }
956
+ return this.aggregateLinesToChunks(linesWithMetadata);
957
+ }
958
+ createDocuments(texts, metadatas) {
959
+ const _metadatas = metadatas || Array(texts.length).fill({});
960
+ const documents = [];
961
+ texts.forEach((text, i) => {
962
+ this.splitText({ text }).forEach((chunk) => {
963
+ const metadata = { ..._metadatas[i], ...chunk.metadata };
964
+ documents.push(
965
+ new Document({
966
+ text: chunk.text,
967
+ metadata
968
+ })
969
+ );
970
+ });
971
+ });
972
+ return documents;
973
+ }
974
+ transformDocuments(documents) {
975
+ const texts = [];
976
+ const metadatas = [];
977
+ for (const doc of documents) {
978
+ texts.push(doc.text);
979
+ metadatas.push(doc.metadata);
980
+ }
981
+ return this.createDocuments(texts, metadatas);
982
+ }
983
+ };
984
+ function splitTextOnTokens({ text, tokenizer }) {
985
+ const splits = [];
986
+ const inputIds = tokenizer.encode(text);
987
+ let startIdx = 0;
988
+ let curIdx = Math.min(startIdx + tokenizer.tokensPerChunk, inputIds.length);
989
+ let chunkIds = inputIds.slice(startIdx, curIdx);
990
+ while (startIdx < inputIds.length) {
991
+ splits.push(tokenizer.decode(chunkIds));
992
+ if (curIdx === inputIds.length) {
993
+ break;
994
+ }
995
+ startIdx += tokenizer.tokensPerChunk - tokenizer.overlap;
996
+ curIdx = Math.min(startIdx + tokenizer.tokensPerChunk, inputIds.length);
997
+ chunkIds = inputIds.slice(startIdx, curIdx);
998
+ }
999
+ return splits;
1000
+ }
1001
+ var TokenTransformer = class _TokenTransformer extends TextTransformer {
1002
+ tokenizer;
1003
+ allowedSpecial;
1004
+ disallowedSpecial;
1005
+ constructor({
1006
+ encodingName = "cl100k_base",
1007
+ modelName,
1008
+ allowedSpecial = /* @__PURE__ */ new Set(),
1009
+ disallowedSpecial = "all",
1010
+ options = {}
1011
+ }) {
1012
+ super(options);
1013
+ try {
1014
+ this.tokenizer = modelName ? encodingForModel(modelName) : getEncoding(encodingName);
1015
+ } catch {
1016
+ throw new Error("Could not load tiktoken encoding. Please install it with `npm install js-tiktoken`.");
1017
+ }
1018
+ this.allowedSpecial = allowedSpecial;
1019
+ this.disallowedSpecial = disallowedSpecial;
1020
+ }
1021
+ splitText({ text }) {
1022
+ const encode = (text2) => {
1023
+ const allowed = this.allowedSpecial === "all" ? "all" : Array.from(this.allowedSpecial);
1024
+ const disallowed = this.disallowedSpecial === "all" ? "all" : Array.from(this.disallowedSpecial);
1025
+ const processedText = this.stripWhitespace ? text2.trim() : text2;
1026
+ return Array.from(this.tokenizer.encode(processedText, allowed, disallowed));
1027
+ };
1028
+ const decode = (tokens) => {
1029
+ const text2 = this.tokenizer.decode(tokens);
1030
+ return this.stripWhitespace ? text2.trim() : text2;
1031
+ };
1032
+ const tokenizer = {
1033
+ overlap: this.overlap,
1034
+ tokensPerChunk: this.size,
1035
+ decode,
1036
+ encode
1037
+ };
1038
+ return splitTextOnTokens({ text, tokenizer });
1039
+ }
1040
+ static fromTikToken({
1041
+ encodingName = "cl100k_base",
1042
+ modelName,
1043
+ options = {}
1044
+ }) {
1045
+ let tokenizer;
1046
+ try {
1047
+ if (modelName) {
1048
+ tokenizer = encodingForModel(modelName);
1049
+ } else {
1050
+ tokenizer = getEncoding(encodingName);
1051
+ }
1052
+ } catch {
1053
+ throw new Error("Could not load tiktoken encoding. Please install it with `npm install js-tiktoken`.");
1054
+ }
1055
+ const tikTokenEncoder = (text) => {
1056
+ const allowed = options.allowedSpecial === "all" ? "all" : options.allowedSpecial ? Array.from(options.allowedSpecial) : [];
1057
+ const disallowed = options.disallowedSpecial === "all" ? "all" : options.disallowedSpecial ? Array.from(options.disallowedSpecial) : [];
1058
+ return tokenizer.encode(text, allowed, disallowed).length;
1059
+ };
1060
+ return new _TokenTransformer({
1061
+ encodingName,
1062
+ modelName,
1063
+ allowedSpecial: options.allowedSpecial,
1064
+ disallowedSpecial: options.disallowedSpecial,
1065
+ options: {
1066
+ size: options.size,
1067
+ overlap: options.overlap,
1068
+ lengthFunction: tikTokenEncoder
1069
+ }
1070
+ });
1071
+ }
1072
+ };
1073
+
1074
+ // src/document/document.ts
1075
+ var MDocument = class _MDocument {
1076
+ chunks;
1077
+ type;
1078
+ // e.g., 'text', 'html', 'markdown', 'json'
1079
+ constructor({ docs, type }) {
1080
+ this.chunks = docs.map((d) => {
1081
+ return new Document({ text: d.text, metadata: d.metadata });
1082
+ });
1083
+ this.type = type;
1084
+ }
1085
+ async extractMetadata({ title, summary, questions, keywords }) {
1086
+ const transformations = [];
1087
+ if (typeof summary !== "undefined") {
1088
+ transformations.push(new SummaryExtractor(typeof summary === "boolean" ? {} : summary));
1089
+ }
1090
+ if (typeof questions !== "undefined") {
1091
+ transformations.push(new QuestionsAnsweredExtractor(typeof questions === "boolean" ? {} : questions));
1092
+ }
1093
+ if (typeof keywords !== "undefined") {
1094
+ transformations.push(new KeywordExtractor(typeof keywords === "boolean" ? {} : keywords));
1095
+ }
1096
+ if (typeof title !== "undefined") {
1097
+ transformations.push(new TitleExtractor(typeof title === "boolean" ? {} : title));
1098
+ }
1099
+ const pipeline = new IngestionPipeline({
1100
+ transformations
1101
+ });
1102
+ const nodes = await pipeline.run({
1103
+ documents: this.chunks
1104
+ });
1105
+ this.chunks = this.chunks.map((doc, i) => {
1106
+ return new Document({
1107
+ text: doc.text,
1108
+ metadata: {
1109
+ ...doc.metadata,
1110
+ ...nodes?.[i]?.metadata || {}
1111
+ }
1112
+ });
1113
+ });
1114
+ return this;
1115
+ }
1116
+ static fromText(text, metadata) {
1117
+ return new _MDocument({
1118
+ docs: [
1119
+ {
1120
+ text,
1121
+ metadata
1122
+ }
1123
+ ],
1124
+ type: "text"
1125
+ });
1126
+ }
1127
+ static fromHTML(html, metadata) {
1128
+ return new _MDocument({
1129
+ docs: [
1130
+ {
1131
+ text: html,
1132
+ metadata
1133
+ }
1134
+ ],
1135
+ type: "html"
1136
+ });
1137
+ }
1138
+ static fromMarkdown(markdown, metadata) {
1139
+ return new _MDocument({
1140
+ docs: [
1141
+ {
1142
+ text: markdown,
1143
+ metadata
1144
+ }
1145
+ ],
1146
+ type: "markdown"
1147
+ });
1148
+ }
1149
+ static fromJSON(jsonString, metadata) {
1150
+ return new _MDocument({
1151
+ docs: [
1152
+ {
1153
+ text: jsonString,
1154
+ metadata
1155
+ }
1156
+ ],
1157
+ type: "json"
1158
+ });
1159
+ }
1160
+ defaultStrategy() {
1161
+ switch (this.type) {
1162
+ case "html":
1163
+ return "html";
1164
+ case "markdown":
1165
+ return "markdown";
1166
+ case "json":
1167
+ return "json";
1168
+ case "latex":
1169
+ return "latex";
1170
+ default:
1171
+ return "recursive";
1172
+ }
1173
+ }
1174
+ async chunkBy(strategy, options) {
1175
+ switch (strategy) {
1176
+ case "recursive":
1177
+ await this.chunkRecursive(options);
1178
+ break;
1179
+ case "character":
1180
+ await this.chunkCharacter(options);
1181
+ break;
1182
+ case "token":
1183
+ await this.chunkToken(options);
1184
+ break;
1185
+ case "markdown":
1186
+ await this.chunkMarkdown(options);
1187
+ break;
1188
+ case "html":
1189
+ await this.chunkHTML(options);
1190
+ break;
1191
+ case "json":
1192
+ await this.chunkJSON(options);
1193
+ break;
1194
+ case "latex":
1195
+ await this.chunkLatex(options);
1196
+ break;
1197
+ default:
1198
+ throw new Error(`Unknown strategy: ${strategy}`);
1199
+ }
1200
+ }
1201
+ async chunkRecursive(options) {
1202
+ if (options?.language) {
1203
+ const rt2 = RecursiveCharacterTransformer.fromLanguage(options.language, options);
1204
+ const textSplit2 = rt2.transformDocuments(this.chunks);
1205
+ this.chunks = textSplit2;
1206
+ return;
1207
+ }
1208
+ const rt = new RecursiveCharacterTransformer({
1209
+ separators: options?.separators,
1210
+ isSeparatorRegex: options?.isSeparatorRegex,
1211
+ options
1212
+ });
1213
+ const textSplit = rt.transformDocuments(this.chunks);
1214
+ this.chunks = textSplit;
1215
+ }
1216
+ async chunkCharacter(options) {
1217
+ const rt = new CharacterTransformer({
1218
+ separator: options?.separator,
1219
+ isSeparatorRegex: options?.isSeparatorRegex,
1220
+ options
1221
+ });
1222
+ const textSplit = rt.transformDocuments(this.chunks);
1223
+ this.chunks = textSplit;
1224
+ }
1225
+ async chunkHTML(options) {
1226
+ if (options?.headers?.length) {
1227
+ const rt = new HTMLHeaderTransformer(options.headers, options?.returnEachLine);
1228
+ const textSplit = rt.transformDocuments(this.chunks);
1229
+ this.chunks = textSplit;
1230
+ return;
1231
+ }
1232
+ if (options?.sections?.length) {
1233
+ const rt = new HTMLSectionTransformer(options.sections);
1234
+ const textSplit = rt.transformDocuments(this.chunks);
1235
+ this.chunks = textSplit;
1236
+ return;
1237
+ }
1238
+ throw new Error("HTML chunking requires either headers or sections to be specified");
1239
+ }
1240
+ async chunkJSON(options) {
1241
+ if (!options?.maxSize) {
1242
+ throw new Error("JSON chunking requires maxSize to be specified");
1243
+ }
1244
+ const rt = new RecursiveJsonTransformer({
1245
+ maxSize: options?.maxSize,
1246
+ minSize: options?.minSize
1247
+ });
1248
+ const textSplit = rt.transformDocuments({
1249
+ documents: this.chunks,
1250
+ ensureAscii: options?.ensureAscii,
1251
+ convertLists: options?.convertLists
1252
+ });
1253
+ this.chunks = textSplit;
1254
+ }
1255
+ async chunkLatex(options) {
1256
+ const rt = new LatexTransformer(options);
1257
+ const textSplit = rt.transformDocuments(this.chunks);
1258
+ this.chunks = textSplit;
1259
+ }
1260
+ async chunkToken(options) {
1261
+ const rt = TokenTransformer.fromTikToken({
1262
+ options,
1263
+ encodingName: options?.encodingName,
1264
+ modelName: options?.modelName
1265
+ });
1266
+ const textSplit = rt.transformDocuments(this.chunks);
1267
+ this.chunks = textSplit;
1268
+ }
1269
+ async chunkMarkdown(options) {
1270
+ if (options?.headers) {
1271
+ const rt2 = new MarkdownHeaderTransformer(options.headers, options?.returnEachLine, options?.stripHeaders);
1272
+ const textSplit2 = rt2.transformDocuments(this.chunks);
1273
+ this.chunks = textSplit2;
1274
+ return;
1275
+ }
1276
+ const rt = new MarkdownTransformer(options);
1277
+ const textSplit = rt.transformDocuments(this.chunks);
1278
+ this.chunks = textSplit;
1279
+ }
1280
+ async chunk(params) {
1281
+ const { strategy: passedStrategy, extract, ...chunkOptions } = params || {};
1282
+ const strategy = passedStrategy || this.defaultStrategy();
1283
+ await this.chunkBy(strategy, chunkOptions);
1284
+ if (extract) {
1285
+ await this.extractMetadata(extract);
1286
+ }
1287
+ return this.chunks;
1288
+ }
1289
+ getDocs() {
1290
+ return this.chunks;
1291
+ }
1292
+ getText() {
1293
+ return this.chunks.map((doc) => doc.text);
1294
+ }
1295
+ getMetadata() {
1296
+ return this.chunks.map((doc) => doc.metadata);
1297
+ }
1298
+ };
1299
+ var DEFAULT_WEIGHTS = {
1300
+ semantic: 0.4,
1301
+ vector: 0.4,
1302
+ position: 0.2
1303
+ };
1304
+ function calculatePositionScore(position, totalChunks) {
1305
+ return 1 - position / totalChunks;
1306
+ }
1307
+ function analyzeQueryEmbedding(embedding) {
1308
+ const magnitude = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0));
1309
+ const dominantFeatures = embedding.map((value, index) => ({ value: Math.abs(value), index })).sort((a, b) => b.value - a.value).slice(0, 5).map((item) => item.index);
1310
+ return { magnitude, dominantFeatures };
1311
+ }
1312
+ function adjustScores(score, queryAnalysis) {
1313
+ const magnitudeAdjustment = queryAnalysis.magnitude > 10 ? 1.1 : 1;
1314
+ const featureStrengthAdjustment = queryAnalysis.magnitude > 5 ? 1.05 : 1;
1315
+ return score * magnitudeAdjustment * featureStrengthAdjustment;
1316
+ }
1317
+ async function rerank(results, query, model, options) {
1318
+ let semanticProvider;
1319
+ if (model.modelId === "rerank-v3.5") {
1320
+ semanticProvider = new CohereRelevanceScorer(model.modelId);
1321
+ } else {
1322
+ semanticProvider = new MastraAgentRelevanceScorer(model.provider, model);
1323
+ }
1324
+ const { queryEmbedding, topK = 3 } = options;
1325
+ const weights = {
1326
+ ...DEFAULT_WEIGHTS,
1327
+ ...options.weights
1328
+ };
1329
+ const totalWeights = Object.values(weights).reduce((sum, weight) => sum + weight, 0);
1330
+ if (totalWeights !== 1) {
1331
+ throw new Error("Weights must add up to 1");
1332
+ }
1333
+ const resultLength = results.length;
1334
+ const queryAnalysis = queryEmbedding ? analyzeQueryEmbedding(queryEmbedding) : null;
1335
+ const scoredResults = await Promise.all(
1336
+ results.map(async (result, index) => {
1337
+ const semanticScore = await semanticProvider.getRelevanceScore(query, result?.metadata?.text);
1338
+ const vectorScore = result.score;
1339
+ const positionScore = calculatePositionScore(index, resultLength);
1340
+ let finalScore = weights.semantic * semanticScore + weights.vector * vectorScore + weights.position * positionScore;
1341
+ if (queryAnalysis) {
1342
+ finalScore = adjustScores(finalScore, queryAnalysis);
1343
+ }
1344
+ return {
1345
+ result,
1346
+ score: finalScore,
1347
+ details: {
1348
+ semantic: semanticScore,
1349
+ vector: vectorScore,
1350
+ position: positionScore,
1351
+ ...queryAnalysis && {
1352
+ queryAnalysis: {
1353
+ magnitude: queryAnalysis.magnitude,
1354
+ dominantFeatures: queryAnalysis.dominantFeatures
1355
+ }
1356
+ }
1357
+ }
1358
+ };
1359
+ })
1360
+ );
1361
+ return scoredResults.sort((a, b) => b.score - a.score).slice(0, topK);
1362
+ }
1363
+
1364
+ // src/graph-rag/index.ts
1365
+ var GraphRAG = class {
1366
+ nodes;
1367
+ edges;
1368
+ dimension;
1369
+ threshold;
1370
+ constructor(dimension = 1536, threshold = 0.7) {
1371
+ this.nodes = /* @__PURE__ */ new Map();
1372
+ this.edges = [];
1373
+ this.dimension = dimension;
1374
+ this.threshold = threshold;
1375
+ }
1376
+ // Add a node to the graph
1377
+ addNode(node) {
1378
+ if (!node.embedding) {
1379
+ throw new Error("Node must have an embedding");
1380
+ }
1381
+ if (node.embedding.length !== this.dimension) {
1382
+ throw new Error(`Embedding dimension must be ${this.dimension}`);
1383
+ }
1384
+ this.nodes.set(node.id, node);
1385
+ }
1386
+ // Add an edge between two nodes
1387
+ addEdge(edge) {
1388
+ if (!this.nodes.has(edge.source) || !this.nodes.has(edge.target)) {
1389
+ throw new Error("Both source and target nodes must exist");
1390
+ }
1391
+ this.edges.push(edge);
1392
+ this.edges.push({
1393
+ source: edge.target,
1394
+ target: edge.source,
1395
+ weight: edge.weight,
1396
+ type: edge.type
1397
+ });
1398
+ }
1399
+ // Helper method to get all nodes
1400
+ getNodes() {
1401
+ return Array.from(this.nodes.values());
1402
+ }
1403
+ // Helper method to get all edges
1404
+ getEdges() {
1405
+ return this.edges;
1406
+ }
1407
+ getEdgesByType(type) {
1408
+ return this.edges.filter((edge) => edge.type === type);
1409
+ }
1410
+ clear() {
1411
+ this.nodes.clear();
1412
+ this.edges = [];
1413
+ }
1414
+ updateNodeContent(id, newContent) {
1415
+ const node = this.nodes.get(id);
1416
+ if (!node) {
1417
+ throw new Error(`Node ${id} not found`);
1418
+ }
1419
+ node.content = newContent;
1420
+ }
1421
+ // Get neighbors of a node
1422
+ getNeighbors(nodeId, edgeType) {
1423
+ return this.edges.filter((edge) => edge.source === nodeId && (!edgeType || edge.type === edgeType)).map((edge) => ({
1424
+ id: edge.target,
1425
+ weight: edge.weight
1426
+ })).filter((node) => node !== void 0);
1427
+ }
1428
+ // Calculate cosine similarity between two vectors
1429
+ cosineSimilarity(vec1, vec2) {
1430
+ if (!vec1 || !vec2) {
1431
+ throw new Error("Vectors must not be null or undefined");
1432
+ }
1433
+ const vectorLength = vec1.length;
1434
+ if (vectorLength !== vec2.length) {
1435
+ throw new Error(`Vector dimensions must match: vec1(${vec1.length}) !== vec2(${vec2.length})`);
1436
+ }
1437
+ let dotProduct = 0;
1438
+ let normVec1 = 0;
1439
+ let normVec2 = 0;
1440
+ for (let i = 0; i < vectorLength; i++) {
1441
+ const a = vec1[i];
1442
+ const b = vec2[i];
1443
+ dotProduct += a * b;
1444
+ normVec1 += a * a;
1445
+ normVec2 += b * b;
1446
+ }
1447
+ const magnitudeProduct = Math.sqrt(normVec1 * normVec2);
1448
+ if (magnitudeProduct === 0) {
1449
+ return 0;
1450
+ }
1451
+ const similarity = dotProduct / magnitudeProduct;
1452
+ return Math.max(-1, Math.min(1, similarity));
1453
+ }
1454
+ createGraph(chunks, embeddings) {
1455
+ if (!chunks?.length || !embeddings?.length) {
1456
+ throw new Error("Chunks and embeddings arrays must not be empty");
1457
+ }
1458
+ if (chunks.length !== embeddings.length) {
1459
+ throw new Error("Chunks and embeddings must have the same length");
1460
+ }
1461
+ chunks.forEach((chunk, index) => {
1462
+ const node = {
1463
+ id: index.toString(),
1464
+ content: chunk.text,
1465
+ embedding: embeddings[index]?.vector,
1466
+ metadata: { ...chunk.metadata }
1467
+ };
1468
+ this.addNode(node);
1469
+ this.nodes.set(node.id, node);
1470
+ });
1471
+ for (let i = 0; i < chunks.length; i++) {
1472
+ const firstEmbedding = embeddings[i]?.vector;
1473
+ for (let j = i + 1; j < chunks.length; j++) {
1474
+ const secondEmbedding = embeddings[j]?.vector;
1475
+ const similarity = this.cosineSimilarity(firstEmbedding, secondEmbedding);
1476
+ if (similarity > this.threshold) {
1477
+ this.addEdge({
1478
+ source: i.toString(),
1479
+ target: j.toString(),
1480
+ weight: similarity,
1481
+ type: "semantic"
1482
+ });
1483
+ }
1484
+ }
1485
+ }
1486
+ }
1487
+ selectWeightedNeighbor(neighbors) {
1488
+ const totalWeight = neighbors.reduce((sum, n) => sum + n.weight, 0);
1489
+ let remainingWeight = Math.random() * totalWeight;
1490
+ for (const neighbor of neighbors) {
1491
+ remainingWeight -= neighbor.weight;
1492
+ if (remainingWeight <= 0) {
1493
+ return neighbor.id;
1494
+ }
1495
+ }
1496
+ return neighbors[neighbors.length - 1]?.id;
1497
+ }
1498
+ // Perform random walk with restart
1499
+ randomWalkWithRestart(startNodeId, steps, restartProb) {
1500
+ const visits = /* @__PURE__ */ new Map();
1501
+ let currentNodeId = startNodeId;
1502
+ for (let step = 0; step < steps; step++) {
1503
+ visits.set(currentNodeId, (visits.get(currentNodeId) || 0) + 1);
1504
+ if (Math.random() < restartProb) {
1505
+ currentNodeId = startNodeId;
1506
+ continue;
1507
+ }
1508
+ const neighbors = this.getNeighbors(currentNodeId);
1509
+ if (neighbors.length === 0) {
1510
+ currentNodeId = startNodeId;
1511
+ continue;
1512
+ }
1513
+ currentNodeId = this.selectWeightedNeighbor(neighbors);
1514
+ }
1515
+ const totalVisits = Array.from(visits.values()).reduce((a, b) => a + b, 0);
1516
+ const normalizedVisits = /* @__PURE__ */ new Map();
1517
+ for (const [nodeId, count] of visits) {
1518
+ normalizedVisits.set(nodeId, count / totalVisits);
1519
+ }
1520
+ return normalizedVisits;
1521
+ }
1522
+ // Retrieve relevant nodes using hybrid approach
1523
+ query({
1524
+ query,
1525
+ topK = 10,
1526
+ randomWalkSteps = 100,
1527
+ restartProb = 0.15
1528
+ }) {
1529
+ if (!query || query.length !== this.dimension) {
1530
+ throw new Error(`Query embedding must have dimension ${this.dimension}`);
1531
+ }
1532
+ if (topK < 1) {
1533
+ throw new Error("TopK must be greater than 0");
1534
+ }
1535
+ if (randomWalkSteps < 1) {
1536
+ throw new Error("Random walk steps must be greater than 0");
1537
+ }
1538
+ if (restartProb <= 0 || restartProb >= 1) {
1539
+ throw new Error("Restart probability must be between 0 and 1");
1540
+ }
1541
+ const similarities = Array.from(this.nodes.values()).map((node) => ({
1542
+ node,
1543
+ similarity: this.cosineSimilarity(query, node.embedding)
1544
+ }));
1545
+ similarities.sort((a, b) => b.similarity - a.similarity);
1546
+ const topNodes = similarities.slice(0, topK);
1547
+ const rerankedNodes = /* @__PURE__ */ new Map();
1548
+ for (const { node, similarity } of topNodes) {
1549
+ const walkScores = this.randomWalkWithRestart(node.id, randomWalkSteps, restartProb);
1550
+ for (const [nodeId, walkScore] of walkScores) {
1551
+ const node2 = this.nodes.get(nodeId);
1552
+ const existingScore = rerankedNodes.get(nodeId)?.score || 0;
1553
+ rerankedNodes.set(nodeId, {
1554
+ node: node2,
1555
+ score: existingScore + similarity * walkScore
1556
+ });
1557
+ }
1558
+ }
1559
+ return Array.from(rerankedNodes.values()).sort((a, b) => b.score - a.score).slice(0, topK).map((item) => ({
1560
+ id: item.node.id,
1561
+ content: item.node.content,
1562
+ metadata: item.node.metadata,
1563
+ score: item.score
1564
+ }));
1565
+ }
1566
+ };
1567
+ var createDocumentChunkerTool = ({
1568
+ doc,
1569
+ params = {
1570
+ strategy: "recursive",
1571
+ size: 512,
1572
+ overlap: 50,
1573
+ separator: "\n"
1574
+ }
1575
+ }) => {
1576
+ return createTool({
1577
+ id: `Document Chunker ${params.strategy} ${params.size}`,
1578
+ inputSchema: z.object({}),
1579
+ description: `Chunks document using ${params.strategy} strategy with size ${params.size} and ${params.overlap} overlap`,
1580
+ execute: async () => {
1581
+ const chunks = await doc.chunk(params);
1582
+ return {
1583
+ chunks
1584
+ };
1585
+ }
1586
+ });
1587
+ };
1588
+ var vectorQuerySearch = async ({
1589
+ indexName,
1590
+ vectorStore,
1591
+ queryText,
1592
+ model,
1593
+ queryFilter = {},
1594
+ topK,
1595
+ includeVectors = false,
1596
+ maxRetries = 2
1597
+ }) => {
1598
+ const { embedding } = await embed({
1599
+ value: queryText,
1600
+ model,
1601
+ maxRetries
1602
+ });
1603
+ const results = await vectorStore.query(indexName, embedding, topK, queryFilter, includeVectors);
1604
+ return { results, queryEmbedding: embedding };
1605
+ };
1606
+
1607
+ // src/utils/default-settings.ts
1608
+ var defaultTopK = `You MUST generate for each query:
1609
+ topK: number of results to return (REQUIRED) (Default: 10)
1610
+ - Generate topK based on exactly what user specifies
1611
+ - must be a valid number
1612
+
1613
+ Notes:
1614
+ - If user provides a valid topK, use the topK provided
1615
+ - If user does not specify topK or provides an invalid topK, use default topK: 10
1616
+ `;
1617
+ var defaultFilter = `You MUST generate for each query:
1618
+ filter: query filter (REQUIRED) (Default: {})
1619
+ - Generate filter based on user's explicit query intent
1620
+ - Must be valid JSON string
1621
+
1622
+ Notes:
1623
+ - If user provides a valid filter, use the filter provided
1624
+ - If user does not specify filter or provides an invalid filter, use default filter: {}
1625
+ `;
1626
+ var defaultVectorQueryDescription = (vectorStoreName, indexName) => `Retrieves relevant information from ${vectorStoreName} using ${indexName} index.
1627
+
1628
+ ${defaultTopK}
1629
+ ${defaultFilter}
1630
+ `;
1631
+ var defaultGraphRagDescription = (vectorStoreName, indexName) => `Fetches and reranks the most relevant chunks using GraphRAG from the ${vectorStoreName} vector store using the ${indexName} index.
1632
+
1633
+ ${defaultTopK}
1634
+ ${defaultFilter}
1635
+ `;
1636
+
1637
+ // src/tools/graph-rag.ts
1638
+ var createGraphRAGTool = ({
1639
+ vectorStoreName,
1640
+ indexName,
1641
+ model,
1642
+ enableFilter = false,
1643
+ graphOptions = {
1644
+ dimension: 1536,
1645
+ randomWalkSteps: 100,
1646
+ restartProb: 0.15,
1647
+ threshold: 0.7
1648
+ },
1649
+ id,
1650
+ description
1651
+ }) => {
1652
+ const toolId = id || `GraphRAG ${vectorStoreName} ${indexName} Tool`;
1653
+ const toolDescription = description || defaultGraphRagDescription(vectorStoreName, indexName);
1654
+ const graphRag = new GraphRAG(graphOptions.dimension, graphOptions.threshold);
1655
+ let isInitialized = false;
1656
+ return createTool({
1657
+ id: toolId,
1658
+ inputSchema: z.object({
1659
+ queryText: z.string(),
1660
+ topK: z.number(),
1661
+ filter: z.string()
1662
+ }),
1663
+ outputSchema: z.object({
1664
+ relevantContext: z.any()
1665
+ }),
1666
+ description: toolDescription,
1667
+ execute: async ({ context: { queryText, topK, filter }, mastra }) => {
1668
+ const vectorStore = mastra?.vectors?.[vectorStoreName];
1669
+ if (vectorStore) {
1670
+ let queryFilter = {};
1671
+ if (enableFilter) {
1672
+ queryFilter = filter ? (() => {
1673
+ try {
1674
+ return JSON.parse(filter);
1675
+ } catch {
1676
+ return filter;
1677
+ }
1678
+ })() : filter;
1679
+ }
1680
+ if (mastra.logger) {
1681
+ mastra.logger.debug("Using this filter and topK:", { queryFilter, topK });
1682
+ }
1683
+ const { results, queryEmbedding } = await vectorQuerySearch({
1684
+ indexName,
1685
+ vectorStore,
1686
+ queryText,
1687
+ model,
1688
+ queryFilter: Object.keys(queryFilter || {}).length > 0 ? queryFilter : void 0,
1689
+ topK,
1690
+ includeVectors: true
1691
+ });
1692
+ if (!isInitialized) {
1693
+ const chunks = results.map((result) => ({
1694
+ text: result?.metadata?.text,
1695
+ metadata: result.metadata ?? {}
1696
+ }));
1697
+ const embeddings = results.map((result) => ({
1698
+ vector: result.vector || []
1699
+ }));
1700
+ graphRag.createGraph(chunks, embeddings);
1701
+ isInitialized = true;
1702
+ }
1703
+ const rerankedResults = graphRag.query({
1704
+ query: queryEmbedding,
1705
+ topK,
1706
+ randomWalkSteps: graphOptions.randomWalkSteps,
1707
+ restartProb: graphOptions.restartProb
1708
+ });
1709
+ const relevantChunks = rerankedResults.map((result) => result.content);
1710
+ return {
1711
+ relevantContext: relevantChunks
1712
+ };
1713
+ }
1714
+ return {
1715
+ relevantContext: []
1716
+ };
1717
+ }
1718
+ });
1719
+ };
1720
+ var createVectorQueryTool = ({
1721
+ vectorStoreName,
1722
+ indexName,
1723
+ model,
1724
+ enableFilter = false,
1725
+ reranker,
1726
+ id,
1727
+ description
1728
+ }) => {
1729
+ const toolId = id || `VectorQuery ${vectorStoreName} ${indexName} Tool`;
1730
+ const toolDescription = description || defaultVectorQueryDescription(vectorStoreName, indexName);
1731
+ return createTool({
1732
+ id: toolId,
1733
+ inputSchema: z.object({
1734
+ queryText: z.string(),
1735
+ topK: z.number(),
1736
+ filter: z.string()
1737
+ }),
1738
+ outputSchema: z.object({
1739
+ relevantContext: z.any()
1740
+ }),
1741
+ description: toolDescription,
1742
+ execute: async ({ context: { queryText, topK, filter }, mastra }) => {
1743
+ const vectorStore = mastra?.vectors?.[vectorStoreName];
1744
+ if (vectorStore) {
1745
+ let queryFilter = {};
1746
+ if (enableFilter) {
1747
+ queryFilter = filter ? (() => {
1748
+ try {
1749
+ return JSON.parse(filter);
1750
+ } catch {
1751
+ return filter;
1752
+ }
1753
+ })() : filter;
1754
+ }
1755
+ if (mastra.logger) {
1756
+ mastra.logger.debug("Using this filter and topK:", { queryFilter, topK });
1757
+ }
1758
+ const { results } = await vectorQuerySearch({
1759
+ indexName,
1760
+ vectorStore,
1761
+ queryText,
1762
+ model,
1763
+ queryFilter: Object.keys(queryFilter || {}).length > 0 ? queryFilter : void 0,
1764
+ topK
1765
+ });
1766
+ if (reranker) {
1767
+ const rerankedResults = await rerank(results, queryText, reranker.model, {
1768
+ ...reranker.options,
1769
+ topK: reranker.options?.topK || topK
1770
+ });
1771
+ const relevantChunks2 = rerankedResults.map(({ result }) => result?.metadata);
1772
+ return { relevantContext: relevantChunks2 };
1773
+ }
1774
+ const relevantChunks = results.map((result) => result?.metadata);
1775
+ return {
1776
+ relevantContext: relevantChunks
1777
+ };
1778
+ }
1779
+ return {
1780
+ relevantContext: []
1781
+ };
1782
+ }
1783
+ });
1784
+ };
1785
+
1786
+ // src/utils/vector-prompts.ts
1787
+ var ASTRA_PROMPT = `When querying Astra, you can ONLY use the operators listed below. Any other operators will be rejected.
1788
+ Important: Don't explain how to construct the filter - use the specified operators and fields to search the content and return relevant results.
1789
+ If a user tries to give an explicit operator that is not supported, reject the filter entirely and let them know that the operator is not supported.
1790
+
1791
+ Basic Comparison Operators:
1792
+ - $eq: Exact match (default when using field: value)
1793
+ Example: { "category": "electronics" }
1794
+ - $ne: Not equal
1795
+ Example: { "category": { "$ne": "electronics" } }
1796
+ - $gt: Greater than
1797
+ Example: { "price": { "$gt": 100 } }
1798
+ - $gte: Greater than or equal
1799
+ Example: { "price": { "$gte": 100 } }
1800
+ - $lt: Less than
1801
+ Example: { "price": { "$lt": 100 } }
1802
+ - $lte: Less than or equal
1803
+ Example: { "price": { "$lte": 100 } }
1804
+
1805
+ Array Operators:
1806
+ - $in: Match any value in array
1807
+ Example: { "category": { "$in": ["electronics", "books"] } }
1808
+ - $nin: Does not match any value in array
1809
+ Example: { "category": { "$nin": ["electronics", "books"] } }
1810
+ - $all: Match all values in array
1811
+ Example: { "tags": { "$all": ["premium", "sale"] } }
1812
+
1813
+ Logical Operators:
1814
+ - $and: Logical AND (can be implicit or explicit)
1815
+ Implicit Example: { "price": { "$gt": 100 }, "category": "electronics" }
1816
+ Explicit Example: { "$and": [{ "price": { "$gt": 100 } }, { "category": "electronics" }] }
1817
+ - $or: Logical OR
1818
+ Example: { "$or": [{ "price": { "$lt": 50 } }, { "category": "books" }] }
1819
+ - $not: Logical NOT
1820
+ Example: { "$not": { "category": "electronics" } }
1821
+
1822
+ Element Operators:
1823
+ - $exists: Check if field exists
1824
+ Example: { "rating": { "$exists": true } }
1825
+
1826
+ Special Operators:
1827
+ - $size: Array length check
1828
+ Example: { "tags": { "$size": 2 } }
1829
+
1830
+ Restrictions:
1831
+ - Regex patterns are not supported
1832
+ - Only $and, $or, and $not logical operators are supported
1833
+ - Nested fields are supported using dot notation
1834
+ - Multiple conditions on the same field are supported with both implicit and explicit $and
1835
+ - Empty arrays in $in/$nin will return no results
1836
+ - A non-empty array is required for $all operator
1837
+ - Only logical operators ($and, $or, $not) can be used at the top level
1838
+ - All other operators must be used within a field condition
1839
+ Valid: { "field": { "$gt": 100 } }
1840
+ Valid: { "$and": [...] }
1841
+ Invalid: { "$gt": 100 }
1842
+ - Logical operators must contain field conditions, not direct operators
1843
+ Valid: { "$and": [{ "field": { "$gt": 100 } }] }
1844
+ Invalid: { "$and": [{ "$gt": 100 }] }
1845
+ - $not operator:
1846
+ - Must be an object
1847
+ - Cannot be empty
1848
+ - Can be used at field level or top level
1849
+ - Valid: { "$not": { "field": "value" } }
1850
+ - Valid: { "field": { "$not": { "$eq": "value" } } }
1851
+ - Other logical operators ($and, $or):
1852
+ - Can only be used at top level or nested within other logical operators
1853
+ - Can not be used on a field level, or be nested inside a field
1854
+ - Can not be used inside an operator
1855
+ - Valid: { "$and": [{ "field": { "$gt": 100 } }] }
1856
+ - Valid: { "$or": [{ "$and": [{ "field": { "$gt": 100 } }] }] }
1857
+ - Invalid: { "field": { "$and": [{ "$gt": 100 }] } }
1858
+ - Invalid: { "field": { "$or": [{ "$gt": 100 }] } }
1859
+ - Invalid: { "field": { "$gt": { "$and": [{...}] } } }
1860
+
1861
+ Example Complex Query:
1862
+ {
1863
+ "$and": [
1864
+ { "category": { "$in": ["electronics", "computers"] } },
1865
+ { "price": { "$gte": 100, "$lte": 1000 } },
1866
+ { "tags": { "$all": ["premium"] } },
1867
+ { "rating": { "$exists": true, "$gt": 4 } },
1868
+ { "$or": [
1869
+ { "stock": { "$gt": 0 } },
1870
+ { "preorder": true }
1871
+ ]}
1872
+ ]
1873
+ }`;
1874
+ var CHROMA_PROMPT = `When querying Chroma, you can ONLY use the operators listed below. Any other operators will be rejected.
1875
+ Important: Don't explain how to construct the filter - use the specified operators and fields to search the content and return relevant results.
1876
+ If a user tries to give an explicit operator that is not supported, reject the filter entirely and let them know that the operator is not supported.
1877
+
1878
+ Basic Comparison Operators:
1879
+ - $eq: Exact match (default when using field: value)
1880
+ Example: { "category": "electronics" }
1881
+ - $ne: Not equal
1882
+ Example: { "category": { "$ne": "electronics" } }
1883
+ - $gt: Greater than
1884
+ Example: { "price": { "$gt": 100 } }
1885
+ - $gte: Greater than or equal
1886
+ Example: { "price": { "$gte": 100 } }
1887
+ - $lt: Less than
1888
+ Example: { "price": { "$lt": 100 } }
1889
+ - $lte: Less than or equal
1890
+ Example: { "price": { "$lte": 100 } }
1891
+
1892
+ Array Operators:
1893
+ - $in: Match any value in array
1894
+ Example: { "category": { "$in": ["electronics", "books"] } }
1895
+ - $nin: Does not match any value in array
1896
+ Example: { "category": { "$nin": ["electronics", "books"] } }
1897
+
1898
+ Logical Operators:
1899
+ - $and: Logical AND
1900
+ Example: { "$and": [{ "price": { "$gt": 100 } }, { "category": "electronics" }] }
1901
+ - $or: Logical OR
1902
+ Example: { "$or": [{ "price": { "$lt": 50 } }, { "category": "books" }] }
1903
+
1904
+ Restrictions:
1905
+ - Regex patterns are not supported
1906
+ - Element operators are not supported
1907
+ - Only $and and $or logical operators are supported
1908
+ - Nested fields are supported using dot notation
1909
+ - Multiple conditions on the same field are supported with both implicit and explicit $and
1910
+ - Empty arrays in $in/$nin will return no results
1911
+ - If multiple top-level fields exist, they're wrapped in $and
1912
+ - Only logical operators ($and, $or) can be used at the top level
1913
+ - All other operators must be used within a field condition
1914
+ Valid: { "field": { "$gt": 100 } }
1915
+ Valid: { "$and": [...] }
1916
+ Invalid: { "$gt": 100 }
1917
+ Invalid: { "$in": [...] }
1918
+ - Logical operators must contain field conditions, not direct operators
1919
+ Valid: { "$and": [{ "field": { "$gt": 100 } }] }
1920
+ Invalid: { "$and": [{ "$gt": 100 }] }
1921
+ - Logical operators ($and, $or):
1922
+ - Can only be used at top level or nested within other logical operators
1923
+ - Can not be used on a field level, or be nested inside a field
1924
+ - Can not be used inside an operator
1925
+ - Valid: { "$and": [{ "field": { "$gt": 100 } }] }
1926
+ - Valid: { "$or": [{ "$and": [{ "field": { "$gt": 100 } }] }] }
1927
+ - Invalid: { "field": { "$and": [{ "$gt": 100 }] } }
1928
+ - Invalid: { "field": { "$or": [{ "$gt": 100 }] } }
1929
+ - Invalid: { "field": { "$gt": { "$and": [{...}] } } }
1930
+ Example Complex Query:
1931
+ {
1932
+ "$and": [
1933
+ { "category": { "$in": ["electronics", "computers"] } },
1934
+ { "price": { "$gte": 100, "$lte": 1000 } },
1935
+ { "$or": [
1936
+ { "inStock": true },
1937
+ { "preorder": true }
1938
+ ]}
1939
+ ]
1940
+ }`;
1941
+ var LIBSQL_PROMPT = `When querying LibSQL Vector, you can ONLY use the operators listed below. Any other operators will be rejected.
1942
+ Important: Don't explain how to construct the filter - use the specified operators and fields to search the content and return relevant results.
1943
+ If a user tries to give an explicit operator that is not supported, reject the filter entirely and let them know that the operator is not supported.
1944
+
1945
+ Basic Comparison Operators:
1946
+ - $eq: Exact match (default when using field: value)
1947
+ Example: { "category": "electronics" }
1948
+ - $ne: Not equal
1949
+ Example: { "category": { "$ne": "electronics" } }
1950
+ - $gt: Greater than
1951
+ Example: { "price": { "$gt": 100 } }
1952
+ - $gte: Greater than or equal
1953
+ Example: { "price": { "$gte": 100 } }
1954
+ - $lt: Less than
1955
+ Example: { "price": { "$lt": 100 } }
1956
+ - $lte: Less than or equal
1957
+ Example: { "price": { "$lte": 100 } }
1958
+
1959
+ Array Operators:
1960
+ - $in: Match any value in array
1961
+ Example: { "category": { "$in": ["electronics", "books"] } }
1962
+ - $nin: Does not match any value in array
1963
+ Example: { "category": { "$nin": ["electronics", "books"] } }
1964
+ - $all: Match all values in array
1965
+ Example: { "tags": { "$all": ["premium", "sale"] } }
1966
+ - $elemMatch: Match array elements that meet all specified conditions
1967
+ Example: { "items": { "$elemMatch": { "price": { "$gt": 100 } } } }
1968
+ - $contains: Check if array contains value
1969
+ Example: { "tags": { "$contains": "premium" } }
1970
+
1971
+ Logical Operators:
1972
+ - $and: Logical AND (implicit when using multiple conditions)
1973
+ Example: { "$and": [{ "price": { "$gt": 100 } }, { "category": "electronics" }] }
1974
+ - $or: Logical OR
1975
+ Example: { "$or": [{ "price": { "$lt": 50 } }, { "category": "books" }] }
1976
+ - $not: Logical NOT
1977
+ Example: { "$not": { "category": "electronics" } }
1978
+ - $nor: Logical NOR
1979
+ Example: { "$nor": [{ "price": { "$lt": 50 } }, { "category": "books" }] }
1980
+
1981
+ Element Operators:
1982
+ - $exists: Check if field exists
1983
+ Example: { "rating": { "$exists": true } }
1984
+
1985
+ Special Operators:
1986
+ - $size: Array length check
1987
+ Example: { "tags": { "$size": 2 } }
1988
+
1989
+ Restrictions:
1990
+ - Regex patterns are not supported
1991
+ - Direct RegExp patterns will throw an error
1992
+ - Nested fields are supported using dot notation
1993
+ - Multiple conditions on the same field are supported with both implicit and explicit $and
1994
+ - Array operations work on array fields only
1995
+ - Basic operators handle array values as JSON strings
1996
+ - Empty arrays in conditions are handled gracefully
1997
+ - Only logical operators ($and, $or, $not, $nor) can be used at the top level
1998
+ - All other operators must be used within a field condition
1999
+ Valid: { "field": { "$gt": 100 } }
2000
+ Valid: { "$and": [...] }
2001
+ Invalid: { "$gt": 100 }
2002
+ Invalid: { "$contains": "value" }
2003
+ - Logical operators must contain field conditions, not direct operators
2004
+ Valid: { "$and": [{ "field": { "$gt": 100 } }] }
2005
+ Invalid: { "$and": [{ "$gt": 100 }] }
2006
+ - $not operator:
2007
+ - Must be an object
2008
+ - Cannot be empty
2009
+ - Can be used at field level or top level
2010
+ - Valid: { "$not": { "field": "value" } }
2011
+ - Valid: { "field": { "$not": { "$eq": "value" } } }
2012
+ - Other logical operators ($and, $or, $nor):
2013
+ - Can only be used at top level or nested within other logical operators
2014
+ - Can not be used on a field level, or be nested inside a field
2015
+ - Can not be used inside an operator
2016
+ - Valid: { "$and": [{ "field": { "$gt": 100 } }] }
2017
+ - Valid: { "$or": [{ "$and": [{ "field": { "$gt": 100 } }] }] }
2018
+ - Invalid: { "field": { "$and": [{ "$gt": 100 }] } }
2019
+ - Invalid: { "field": { "$or": [{ "$gt": 100 }] } }
2020
+ - Invalid: { "field": { "$gt": { "$and": [{...}] } } }
2021
+ - $elemMatch requires an object with conditions
2022
+ Valid: { "array": { "$elemMatch": { "field": "value" } } }
2023
+ Invalid: { "array": { "$elemMatch": "value" } }
2024
+
2025
+ Example Complex Query:
2026
+ {
2027
+ "$and": [
2028
+ { "category": { "$in": ["electronics", "computers"] } },
2029
+ { "price": { "$gte": 100, "$lte": 1000 } },
2030
+ { "tags": { "$all": ["premium", "sale"] } },
2031
+ { "items": { "$elemMatch": { "price": { "$gt": 50 }, "inStock": true } } },
2032
+ { "$or": [
2033
+ { "stock": { "$gt": 0 } },
2034
+ { "preorder": true }
2035
+ ]}
2036
+ ]
2037
+ }`;
2038
+ var PGVECTOR_PROMPT = `When querying PG Vector, you can ONLY use the operators listed below. Any other operators will be rejected.
2039
+ Important: Don't explain how to construct the filter - use the specified operators and fields to search the content and return relevant results.
2040
+ If a user tries to give an explicit operator that is not supported, reject the filter entirely and let them know that the operator is not supported.
2041
+
2042
+ Basic Comparison Operators:
2043
+ - $eq: Exact match (default when using field: value)
2044
+ Example: { "category": "electronics" }
2045
+ - $ne: Not equal
2046
+ Example: { "category": { "$ne": "electronics" } }
2047
+ - $gt: Greater than
2048
+ Example: { "price": { "$gt": 100 } }
2049
+ - $gte: Greater than or equal
2050
+ Example: { "price": { "$gte": 100 } }
2051
+ - $lt: Less than
2052
+ Example: { "price": { "$lt": 100 } }
2053
+ - $lte: Less than or equal
2054
+ Example: { "price": { "$lte": 100 } }
2055
+
2056
+ Array Operators:
2057
+ - $in: Match any value in array
2058
+ Example: { "category": { "$in": ["electronics", "books"] } }
2059
+ - $nin: Does not match any value in array
2060
+ Example: { "category": { "$nin": ["electronics", "books"] } }
2061
+ - $all: Match all values in array
2062
+ Example: { "tags": { "$all": ["premium", "sale"] } }
2063
+ - $elemMatch: Match array elements that meet all specified conditions
2064
+ Example: { "items": { "$elemMatch": { "price": { "$gt": 100 } } } }
2065
+ - $contains: Check if array contains value
2066
+ Example: { "tags": { "$contains": "premium" } }
2067
+
2068
+ Logical Operators:
2069
+ - $and: Logical AND
2070
+ Example: { "$and": [{ "price": { "$gt": 100 } }, { "category": "electronics" }] }
2071
+ - $or: Logical OR
2072
+ Example: { "$or": [{ "price": { "$lt": 50 } }, { "category": "books" }] }
2073
+ - $not: Logical NOT
2074
+ Example: { "$not": { "category": "electronics" } }
2075
+ - $nor: Logical NOR
2076
+ Example: { "$nor": [{ "price": { "$lt": 50 } }, { "category": "books" }] }
2077
+
2078
+ Element Operators:
2079
+ - $exists: Check if field exists
2080
+ Example: { "rating": { "$exists": true } }
2081
+
2082
+ Special Operators:
2083
+ - $size: Array length check
2084
+ Example: { "tags": { "$size": 2 } }
2085
+ - $regex: Pattern matching (PostgreSQL regex syntax)
2086
+ Example: { "name": { "$regex": "^iphone" } }
2087
+ - $options: Regex options (used with $regex)
2088
+ Example: { "name": { "$regex": "iphone", "$options": "i" } }
2089
+
2090
+ Restrictions:
2091
+ - Direct RegExp patterns are supported
2092
+ - Nested fields are supported using dot notation
2093
+ - Multiple conditions on the same field are supported with both implicit and explicit $and
2094
+ - Array operations work on array fields only
2095
+ - Regex patterns must follow PostgreSQL syntax
2096
+ - Empty arrays in conditions are handled gracefully
2097
+ - Only logical operators ($and, $or, $not, $nor) can be used at the top level
2098
+ - All other operators must be used within a field condition
2099
+ Valid: { "field": { "$gt": 100 } }
2100
+ Valid: { "$and": [...] }
2101
+ Invalid: { "$gt": 100 }
2102
+ Invalid: { "$regex": "pattern" }
2103
+ - Logical operators must contain field conditions, not direct operators
2104
+ Valid: { "$and": [{ "field": { "$gt": 100 } }] }
2105
+ Invalid: { "$and": [{ "$gt": 100 }] }
2106
+ - $not operator:
2107
+ - Must be an object
2108
+ - Cannot be empty
2109
+ - Can be used at field level or top level
2110
+ - Valid: { "$not": { "field": "value" } }
2111
+ - Valid: { "field": { "$not": { "$eq": "value" } } }
2112
+ - Other logical operators ($and, $or, $nor):
2113
+ - Can only be used at top level or nested within other logical operators
2114
+ - Can not be used on a field level, or be nested inside a field
2115
+ - Can not be used inside an operator
2116
+ - Valid: { "$and": [{ "field": { "$gt": 100 } }] }
2117
+ - Valid: { "$or": [{ "$and": [{ "field": { "$gt": 100 } }] }] }
2118
+ - Invalid: { "field": { "$and": [{ "$gt": 100 }] } }
2119
+ - Invalid: { "field": { "$or": [{ "$gt": 100 }] } }
2120
+ - Invalid: { "field": { "$gt": { "$and": [{...}] } } }
2121
+ - $elemMatch requires an object with conditions
2122
+ Valid: { "array": { "$elemMatch": { "field": "value" } } }
2123
+ Invalid: { "array": { "$elemMatch": "value" } }
2124
+
2125
+ Example Complex Query:
2126
+ {
2127
+ "$and": [
2128
+ { "category": { "$in": ["electronics", "computers"] } },
2129
+ { "price": { "$gte": 100, "$lte": 1000 } },
2130
+ { "tags": { "$all": ["premium", "sale"] } },
2131
+ { "items": { "$elemMatch": { "price": { "$gt": 50 }, "inStock": true } } },
2132
+ { "$or": [
2133
+ { "name": { "$regex": "^iphone", "$options": "i" } },
2134
+ { "description": { "$regex": ".*apple.*" } }
2135
+ ]}
2136
+ ]
2137
+ }`;
2138
+ var PINECONE_PROMPT = `When querying Pinecone, you can ONLY use the operators listed below. Any other operators will be rejected.
2139
+ Important: Don't explain how to construct the filter - use the specified operators and fields to search the content and return relevant results.
2140
+ If a user tries to give an explicit operator that is not supported, reject the filter entirely and let them know that the operator is not supported.
2141
+
2142
+ Basic Comparison Operators:
2143
+ - $eq: Exact match (default when using field: value)
2144
+ Example: { "category": "electronics" }
2145
+ - $ne: Not equal
2146
+ Example: { "category": { "$ne": "electronics" } }
2147
+ - $gt: Greater than
2148
+ Example: { "price": { "$gt": 100 } }
2149
+ - $gte: Greater than or equal
2150
+ Example: { "price": { "$gte": 100 } }
2151
+ - $lt: Less than
2152
+ Example: { "price": { "$lt": 100 } }
2153
+ - $lte: Less than or equal
2154
+ Example: { "price": { "$lte": 100 } }
2155
+
2156
+ Array Operators:
2157
+ - $in: Match any value in array
2158
+ Example: { "category": { "$in": ["electronics", "books"] } }
2159
+ - $nin: Does not match any value in array
2160
+ Example: { "category": { "$nin": ["electronics", "books"] } }
2161
+ - $all: Match all values in array
2162
+ Example: { "tags": { "$all": ["premium", "sale"] } }
2163
+
2164
+ Logical Operators:
2165
+ - $and: Logical AND (can be implicit or explicit)
2166
+ Implicit Example: { "price": { "$gt": 100 }, "category": "electronics" }
2167
+ Explicit Example: { "$and": [{ "price": { "$gt": 100 } }, { "category": "electronics" }] }
2168
+ - $or: Logical OR
2169
+ Example: { "$or": [{ "price": { "$lt": 50 } }, { "category": "books" }] }
2170
+
2171
+ Element Operators:
2172
+ - $exists: Check if field exists
2173
+ Example: { "rating": { "$exists": true } }
2174
+
2175
+ Restrictions:
2176
+ - Regex patterns are not supported
2177
+ - Only $and and $or logical operators are supported at the top level
2178
+ - Empty arrays in $in/$nin will return no results
2179
+ - A non-empty array is required for $all operator
2180
+ - Nested fields are supported using dot notation
2181
+ - Multiple conditions on the same field are supported with both implicit and explicit $and
2182
+ - At least one key-value pair is required in filter object
2183
+ - Empty objects and undefined values are treated as no filter
2184
+ - Invalid types in comparison operators will throw errors
2185
+ - All non-logical operators must be used within a field condition
2186
+ Valid: { "field": { "$gt": 100 } }
2187
+ Valid: { "$and": [...] }
2188
+ Invalid: { "$gt": 100 }
2189
+ - Logical operators must contain field conditions, not direct operators
2190
+ Valid: { "$and": [{ "field": { "$gt": 100 } }] }
2191
+ Invalid: { "$and": [{ "$gt": 100 }] }
2192
+ - Logical operators ($and, $or):
2193
+ - Can only be used at top level or nested within other logical operators
2194
+ - Can not be used on a field level, or be nested inside a field
2195
+ - Can not be used inside an operator
2196
+ - Valid: { "$and": [{ "field": { "$gt": 100 } }] }
2197
+ - Valid: { "$or": [{ "$and": [{ "field": { "$gt": 100 } }] }] }
2198
+ - Invalid: { "field": { "$and": [{ "$gt": 100 }] } }
2199
+ - Invalid: { "field": { "$or": [{ "$gt": 100 }] } }
2200
+ - Invalid: { "field": { "$gt": { "$and": [{...}] } } }
2201
+ Example Complex Query:
2202
+ {
2203
+ "$and": [
2204
+ { "category": { "$in": ["electronics", "computers"] } },
2205
+ { "price": { "$gte": 100, "$lte": 1000 } },
2206
+ { "tags": { "$all": ["premium", "sale"] } },
2207
+ { "rating": { "$exists": true, "$gt": 4 } },
2208
+ { "$or": [
2209
+ { "stock": { "$gt": 0 } },
2210
+ { "preorder": true }
2211
+ ]}
2212
+ ]
2213
+ }`;
2214
+ var QDRANT_PROMPT = `When querying Qdrant, you can ONLY use the operators listed below. Any other operators will be rejected.
2215
+ Important: Don't explain how to construct the filter - use the specified operators and fields to search the content and return relevant results.
2216
+ If a user tries to give an explicit operator that is not supported, reject the filter entirely and let them know that the operator is not supported.
2217
+
2218
+ Basic Comparison Operators:
2219
+ - $eq: Exact match (default when using field: value)
2220
+ Example: { "category": "electronics" }
2221
+ - $ne: Not equal
2222
+ Example: { "category": { "$ne": "electronics" } }
2223
+ - $gt: Greater than
2224
+ Example: { "price": { "$gt": 100 } }
2225
+ - $gte: Greater than or equal
2226
+ Example: { "price": { "$gte": 100 } }
2227
+ - $lt: Less than
2228
+ Example: { "price": { "$lt": 100 } }
2229
+ - $lte: Less than or equal
2230
+ Example: { "price": { "$lte": 100 } }
2231
+
2232
+ Array Operators:
2233
+ - $in: Match any value in array
2234
+ Example: { "category": { "$in": ["electronics", "books"] } }
2235
+ - $nin: Does not match any value in array
2236
+ Example: { "category": { "$nin": ["electronics", "books"] } }
2237
+
2238
+ Logical Operators:
2239
+ - $and: Logical AND (implicit when using multiple conditions)
2240
+ Example: { "$and": [{ "price": { "$gt": 100 } }, { "category": "electronics" }] }
2241
+ - $or: Logical OR
2242
+ Example: { "$or": [{ "price": { "$lt": 50 } }, { "category": "books" }] }
2243
+ - $not: Logical NOT
2244
+ Example: { "$not": { "category": "electronics" } }
2245
+
2246
+ Element Operators:
2247
+ - $exists: Check if field exists
2248
+ Example: { "rating": { "$exists": true } }
2249
+
2250
+ Special Operators:
2251
+ - $regex: Pattern matching
2252
+ Example: { "name": { "$regex": "iphone.*" } }
2253
+ - $count: Array length/value count
2254
+ Example: { "tags": { "$count": { "$gt": 2 } } }
2255
+ - $geo: Geographical filters (supports radius, box, polygon)
2256
+ Example: {
2257
+ "location": {
2258
+ "$geo": {
2259
+ "type": "radius",
2260
+ "center": { "lat": 52.5, "lon": 13.4 },
2261
+ "radius": 10000
2262
+ }
2263
+ }
2264
+ }
2265
+ - $hasId: Match specific document IDs
2266
+ Example: { "$hasId": ["doc1", "doc2"] }
2267
+ - $hasVector: Check vector existence
2268
+ Example: { "$hasVector": "" }
2269
+ - $datetime: RFC 3339 datetime range
2270
+ Example: {
2271
+ "created_at": {
2272
+ "$datetime": {
2273
+ "range": {
2274
+ "gt": "2024-01-01T00:00:00Z",
2275
+ "lt": "2024-12-31T23:59:59Z"
2276
+ }
2277
+ }
2278
+ }
2279
+ }
2280
+ - $null: Check for null values
2281
+ Example: { "field": { "$null": true } }
2282
+ - $empty: Check for empty values
2283
+ Example: { "array": { "$empty": true } }
2284
+ - $nested: Nested object filters
2285
+ Example: {
2286
+ "items[]": {
2287
+ "$nested": {
2288
+ "price": { "$gt": 100 },
2289
+ "stock": { "$gt": 0 }
2290
+ }
2291
+ }
2292
+ }
2293
+
2294
+ Restrictions:
2295
+ - Only logical operators ($and, $or, $not) and collection operators ($hasId, $hasVector) can be used at the top level
2296
+ - All other operators must be used within a field condition
2297
+ Valid: { "field": { "$gt": 100 } }
2298
+ Valid: { "$and": [...] }
2299
+ Valid: { "$hasId": [...] }
2300
+ Invalid: { "$gt": 100 }
2301
+ - Nested fields are supported using dot notation
2302
+ - Array fields with nested objects use [] suffix: "items[]"
2303
+ - Geo filtering requires specific format for radius, box, or polygon
2304
+ - Datetime values must be in RFC 3339 format
2305
+ - Empty arrays in conditions are handled as empty values
2306
+ - Null values are handled with $null operator
2307
+ - Empty values are handled with $empty operator
2308
+ - $regex uses standard regex syntax
2309
+ - $count can only be used with numeric comparison operators
2310
+ - $nested requires an object with conditions
2311
+ - Logical operators must contain field conditions, not direct operators
2312
+ Valid: { "$and": [{ "field": { "$gt": 100 } }] }
2313
+ Invalid: { "$and": [{ "$gt": 100 }] }
2314
+ - $not operator:
2315
+ - Must be an object
2316
+ - Cannot be empty
2317
+ - Can be used at field level or top level
2318
+ - Valid: { "$not": { "field": "value" } }
2319
+ - Valid: { "field": { "$not": { "$eq": "value" } } }
2320
+ - Other logical operators ($and, $or):
2321
+ - Can only be used at top level or nested within other logical operators
2322
+ - Can not be used on a field level, or be nested inside a field
2323
+ - Can not be used inside an operator
2324
+ - Valid: { "$and": [{ "field": { "$gt": 100 } }] }
2325
+ - Valid: { "$or": [{ "$and": [{ "field": { "$gt": 100 } }] }] }
2326
+ - Invalid: { "field": { "$and": [{ "$gt": 100 }] } }
2327
+ - Invalid: { "field": { "$or": [{ "$gt": 100 }] } }
2328
+ - Invalid: { "field": { "$gt": { "$and": [{...}] } } }
2329
+ Example Complex Query:
2330
+ {
2331
+ "$and": [
2332
+ { "category": { "$in": ["electronics"] } },
2333
+ { "price": { "$gt": 100 } },
2334
+ { "location": {
2335
+ "$geo": {
2336
+ "type": "radius",
2337
+ "center": { "lat": 52.5, "lon": 13.4 },
2338
+ "radius": 5000
2339
+ }
2340
+ }},
2341
+ { "items[]": {
2342
+ "$nested": {
2343
+ "price": { "$gt": 50 },
2344
+ "stock": { "$gt": 0 }
2345
+ }
2346
+ }},
2347
+ { "created_at": {
2348
+ "$datetime": {
2349
+ "range": {
2350
+ "gt": "2024-01-01T00:00:00Z"
2351
+ }
2352
+ }
2353
+ }},
2354
+ { "$or": [
2355
+ { "status": { "$ne": "discontinued" } },
2356
+ { "clearance": true }
2357
+ ]}
2358
+ ]
2359
+ }`;
2360
+ var UPSTASH_PROMPT = `When querying Upstash Vector, you can ONLY use the operators listed below. Any other operators will be rejected.
2361
+ Important: Don't explain how to construct the filter - use the specified operators and fields to search the content and return relevant results.
2362
+ If a user tries to give an explicit operator that is not supported, reject the filter entirely and let them know that the operator is not supported.
2363
+
2364
+ Basic Comparison Operators:
2365
+ - $eq: Exact match (default when using field: value)
2366
+ Example: { "category": "electronics" } or { "category": { "$eq": "electronics" } }
2367
+ - $ne: Not equal
2368
+ Example: { "category": { "$ne": "electronics" } }
2369
+ - $gt: Greater than
2370
+ Example: { "price": { "$gt": 100 } }
2371
+ - $gte: Greater than or equal
2372
+ Example: { "price": { "$gte": 100 } }
2373
+ - $lt: Less than
2374
+ Example: { "price": { "$lt": 100 } }
2375
+ - $lte: Less than or equal
2376
+ Example: { "price": { "$lte": 100 } }
2377
+
2378
+ Array Operators:
2379
+ - $in: Match any value in array
2380
+ Example: { "category": { "$in": ["electronics", "books"] } }
2381
+ - $nin: Does not match any value in array
2382
+ Example: { "category": { "$nin": ["electronics", "books"] } }
2383
+ - $all: Matches all values in array
2384
+ Example: { "tags": { "$all": ["premium", "new"] } }
2385
+
2386
+ Logical Operators:
2387
+ - $and: Logical AND (implicit when using multiple conditions)
2388
+ Example: { "$and": [{ "price": { "$gt": 100 } }, { "category": "electronics" }] }
2389
+ - $or: Logical OR
2390
+ Example: { "$or": [{ "price": { "$lt": 50 } }, { "category": "books" }] }
2391
+ - $not: Logical NOT
2392
+ Example: { "$not": { "category": "electronics" } }
2393
+ - $nor: Logical NOR
2394
+ Example: { "$nor": [{ "price": { "$lt": 50 } }, { "category": "books" }] }
2395
+
2396
+ Element Operators:
2397
+ - $exists: Check if field exists
2398
+ Example: { "rating": { "$exists": true } }
2399
+
2400
+ Special Operators:
2401
+ - $regex: Pattern matching using glob syntax (only as operator, not direct RegExp)
2402
+ Example: { "name": { "$regex": "iphone*" } }
2403
+ - $contains: Check if array/string contains value
2404
+ Example: { "tags": { "$contains": "premium" } }
2405
+
2406
+ Restrictions:
2407
+ - Null/undefined values are not supported in any operator
2408
+ - Empty arrays are only supported in $in/$nin operators
2409
+ - Direct RegExp patterns are not supported, use $regex with glob syntax
2410
+ - Nested fields are supported using dot notation
2411
+ - Multiple conditions on same field are combined with AND
2412
+ - String values with quotes are automatically escaped
2413
+ - Only logical operators ($and, $or, $not, $nor) can be used at the top level
2414
+ - All other operators must be used within a field condition
2415
+ Valid: { "field": { "$gt": 100 } }
2416
+ Valid: { "$and": [...] }
2417
+ Invalid: { "$gt": 100 }
2418
+ - $regex uses glob syntax (*, ?) not standard regex patterns
2419
+ - $contains works on both arrays and string fields
2420
+ - Logical operators must contain field conditions, not direct operators
2421
+ Valid: { "$and": [{ "field": { "$gt": 100 } }] }
2422
+ Invalid: { "$and": [{ "$gt": 100 }] }
2423
+ - $not operator:
2424
+ - Must be an object
2425
+ - Cannot be empty
2426
+ - Can be used at field level or top level
2427
+ - Valid: { "$not": { "field": "value" } }
2428
+ - Valid: { "field": { "$not": { "$eq": "value" } } }
2429
+ - Other logical operators ($and, $or, $nor):
2430
+ - Can only be used at top level or nested within other logical operators
2431
+ - Can not be used on a field level, or be nested inside a field
2432
+ - Can not be used inside an operator
2433
+ - Valid: { "$and": [{ "field": { "$gt": 100 } }] }
2434
+ - Valid: { "$or": [{ "$and": [{ "field": { "$gt": 100 } }] }] }
2435
+ - Invalid: { "field": { "$and": [{ "$gt": 100 }] } }
2436
+ - Invalid: { "field": { "$or": [{ "$gt": 100 }] } }
2437
+ - Invalid: { "field": { "$gt": { "$and": [{...}] } } }
2438
+ Example Complex Query:
2439
+ {
2440
+ "$and": [
2441
+ { "category": { "$in": ["electronics", "computers"] } },
2442
+ { "price": { "$gt": 100, "$lt": 1000 } },
2443
+ { "tags": { "$all": ["premium", "new"] } },
2444
+ { "name": { "$regex": "iphone*" } },
2445
+ { "description": { "$contains": "latest" } },
2446
+ { "$or": [
2447
+ { "brand": "Apple" },
2448
+ { "rating": { "$gte": 4.5 } }
2449
+ ]}
2450
+ ]
2451
+ }`;
2452
+ var VECTORIZE_PROMPT = `When querying Vectorize, you can ONLY use the operators listed below. Any other operators will be rejected.
2453
+ Important: Don't explain how to construct the filter - use the specified operators and fields to search the content and return relevant results.
2454
+ If a user tries to give an explicit operator that is not supported, reject the filter entirely and let them know that the operator is not supported.
2455
+
2456
+ Basic Comparison Operators:
2457
+ - $eq: Exact match (default when using field: value)
2458
+ Example: { "category": "electronics" }
2459
+ - $ne: Not equal
2460
+ Example: { "category": { "$ne": "electronics" } }
2461
+ - $gt: Greater than
2462
+ Example: { "price": { "$gt": 100 } }
2463
+ - $gte: Greater than or equal
2464
+ Example: { "price": { "$gte": 100 } }
2465
+ - $lt: Less than
2466
+ Example: { "price": { "$lt": 100 } }
2467
+ - $lte: Less than or equal
2468
+ Example: { "price": { "$lte": 100 } }
2469
+
2470
+ Array Operators:
2471
+ - $in: Match any value in array
2472
+ Example: { "category": { "$in": ["electronics", "books"] } }
2473
+ - $nin: Does not match any value in array
2474
+ Example: { "category": { "$nin": ["electronics", "books"] } }
2475
+
2476
+ Restrictions:
2477
+ - Regex patterns are not supported
2478
+ - Logical operators are not supported
2479
+ - Element operators are not supported
2480
+ - Fields must have a flat structure, as nested fields are not supported
2481
+ - Multiple conditions on the same field are supported
2482
+ - Empty arrays in $in/$nin will return no results
2483
+ - Filter keys cannot be longer than 512 characters
2484
+ - Filter keys cannot contain invalid characters ($, ", empty)
2485
+ - Filter size is limited to prevent oversized queries
2486
+ - Invalid types in operators return no results instead of throwing errors
2487
+ - Empty objects are accepted in filters
2488
+ - Metadata must use flat structure with dot notation (no nested objects)
2489
+ - Must explicitly create metadata indexes for filterable fields (limit 10 per index)
2490
+ - Can only effectively filter on indexed metadata fields
2491
+ - Metadata values can be strings, numbers, booleans, or homogeneous arrays
2492
+ - No operators can be used at the top level (no logical operators supported)
2493
+ - All operators must be used within a field condition
2494
+ Valid: { "field": { "$gt": 100 } }
2495
+ Invalid: { "$gt": 100 }
2496
+ Invalid: { "$in": [...] }
2497
+
2498
+ Example Complex Query:
2499
+ {
2500
+ "category": { "$in": ["electronics", "computers"] },
2501
+ "price": { "$gte": 100, "$lte": 1000 },
2502
+ "inStock": true
2503
+ }`;
2504
+
2505
+ export { ASTRA_PROMPT, CHROMA_PROMPT, GraphRAG, LIBSQL_PROMPT, MDocument, PGVECTOR_PROMPT, PINECONE_PROMPT, QDRANT_PROMPT, UPSTASH_PROMPT, VECTORIZE_PROMPT, createDocumentChunkerTool, createGraphRAGTool, createVectorQueryTool, defaultFilter, defaultGraphRagDescription, defaultTopK, defaultVectorQueryDescription, rerank };