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