@retrivora-ai/rag-engine 0.2.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-CYVPACA7.mjs → chunk-5U2DHPIX.mjs} +45 -5
- package/dist/handlers/index.js +45 -5
- package/dist/handlers/index.mjs +1 -1
- package/dist/server.js +45 -5
- package/dist/server.mjs +1 -1
- package/package.json +1 -1
- package/src/core/Pipeline.ts +70 -6
|
@@ -1556,7 +1556,11 @@ var EmbeddingStrategyResolver = class {
|
|
|
1556
1556
|
function normalizeHintValue(value) {
|
|
1557
1557
|
return value.replace(/\s+/g, " ").trim();
|
|
1558
1558
|
}
|
|
1559
|
+
function isLikelyPromptPhrase(value) {
|
|
1560
|
+
return /^(what|which|who|where|when|why|how)\b/i.test(value.trim());
|
|
1561
|
+
}
|
|
1559
1562
|
function extractQueryFieldHints(question) {
|
|
1563
|
+
var _a, _b, _c;
|
|
1560
1564
|
if (!question.trim()) return [];
|
|
1561
1565
|
const hints = /* @__PURE__ */ new Map();
|
|
1562
1566
|
const addHint = (value, field) => {
|
|
@@ -1573,6 +1577,17 @@ function extractQueryFieldHints(question) {
|
|
|
1573
1577
|
for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
|
|
1574
1578
|
addHint(match[1]);
|
|
1575
1579
|
}
|
|
1580
|
+
const naturalQuestionPatterns = [
|
|
1581
|
+
/\b(?:what|which)\s+(?:is|are|was|were)\s+(?:the\s+)?([^?.!,]{1,60}?)\s+of\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
|
|
1582
|
+
/\b(?:who|what)\s+(?:is|are|was|were)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
|
|
1583
|
+
/\b(?:about|for|regarding)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi
|
|
1584
|
+
];
|
|
1585
|
+
for (const pattern of naturalQuestionPatterns) {
|
|
1586
|
+
for (const match of question.matchAll(pattern)) {
|
|
1587
|
+
const value = (_a = match[2]) != null ? _a : match[1];
|
|
1588
|
+
if (value) addHint(value);
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1576
1591
|
const fieldPattern = `([^\\n:=?.!,]{1,60}?)`;
|
|
1577
1592
|
const valuePattern = `([^\\n?.!,]{1,120}?)`;
|
|
1578
1593
|
const fieldValuePatterns = [
|
|
@@ -1582,11 +1597,37 @@ function extractQueryFieldHints(question) {
|
|
|
1582
1597
|
];
|
|
1583
1598
|
for (const pattern of fieldValuePatterns) {
|
|
1584
1599
|
for (const match of question.matchAll(pattern)) {
|
|
1585
|
-
|
|
1600
|
+
const field = normalizeHintValue((_b = match[1]) != null ? _b : "");
|
|
1601
|
+
const value = (_c = match[2]) != null ? _c : "";
|
|
1602
|
+
if (field && !isLikelyPromptPhrase(field)) {
|
|
1603
|
+
addHint(value, field);
|
|
1604
|
+
} else {
|
|
1605
|
+
addHint(value);
|
|
1606
|
+
}
|
|
1586
1607
|
}
|
|
1587
1608
|
}
|
|
1609
|
+
for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){1,3}\b/g)) {
|
|
1610
|
+
addHint(match[0]);
|
|
1611
|
+
}
|
|
1588
1612
|
return [...hints.values()];
|
|
1589
1613
|
}
|
|
1614
|
+
function buildQueryFilter(question, hints) {
|
|
1615
|
+
const filter = { metadata: {}, keywords: [], queryText: question };
|
|
1616
|
+
for (const hint of hints) {
|
|
1617
|
+
if (hint.field) {
|
|
1618
|
+
filter.metadata[hint.field] = hint.value;
|
|
1619
|
+
} else {
|
|
1620
|
+
filter.keywords.push(hint.value);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g)) {
|
|
1624
|
+
const term = normalizeHintValue(match[0]);
|
|
1625
|
+
if (term && !filter.keywords.includes(term)) filter.keywords.push(term);
|
|
1626
|
+
}
|
|
1627
|
+
if (Object.keys(filter.metadata || {}).length === 0) delete filter.metadata;
|
|
1628
|
+
if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
|
|
1629
|
+
return filter;
|
|
1630
|
+
}
|
|
1590
1631
|
var Pipeline = class {
|
|
1591
1632
|
constructor(config) {
|
|
1592
1633
|
this.initialised = false;
|
|
@@ -1675,10 +1716,9 @@ var Pipeline = class {
|
|
|
1675
1716
|
try {
|
|
1676
1717
|
const queryVector = await this.embeddingProvider.embed(question, { taskType: "query" });
|
|
1677
1718
|
const fieldHints = extractQueryFieldHints(question);
|
|
1678
|
-
const
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
});
|
|
1719
|
+
const filter = buildQueryFilter(question, fieldHints);
|
|
1720
|
+
filter.__fieldHints = fieldHints;
|
|
1721
|
+
const rawMatches = await this.vectorDB.query(queryVector, topK, ns, filter);
|
|
1682
1722
|
const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
|
|
1683
1723
|
const context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
|
|
1684
1724
|
${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
package/dist/handlers/index.js
CHANGED
|
@@ -2680,7 +2680,11 @@ var EmbeddingStrategyResolver = class {
|
|
|
2680
2680
|
function normalizeHintValue(value) {
|
|
2681
2681
|
return value.replace(/\s+/g, " ").trim();
|
|
2682
2682
|
}
|
|
2683
|
+
function isLikelyPromptPhrase(value) {
|
|
2684
|
+
return /^(what|which|who|where|when|why|how)\b/i.test(value.trim());
|
|
2685
|
+
}
|
|
2683
2686
|
function extractQueryFieldHints(question) {
|
|
2687
|
+
var _a, _b, _c;
|
|
2684
2688
|
if (!question.trim()) return [];
|
|
2685
2689
|
const hints = /* @__PURE__ */ new Map();
|
|
2686
2690
|
const addHint = (value, field) => {
|
|
@@ -2697,6 +2701,17 @@ function extractQueryFieldHints(question) {
|
|
|
2697
2701
|
for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
|
|
2698
2702
|
addHint(match[1]);
|
|
2699
2703
|
}
|
|
2704
|
+
const naturalQuestionPatterns = [
|
|
2705
|
+
/\b(?:what|which)\s+(?:is|are|was|were)\s+(?:the\s+)?([^?.!,]{1,60}?)\s+of\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
|
|
2706
|
+
/\b(?:who|what)\s+(?:is|are|was|were)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
|
|
2707
|
+
/\b(?:about|for|regarding)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi
|
|
2708
|
+
];
|
|
2709
|
+
for (const pattern of naturalQuestionPatterns) {
|
|
2710
|
+
for (const match of question.matchAll(pattern)) {
|
|
2711
|
+
const value = (_a = match[2]) != null ? _a : match[1];
|
|
2712
|
+
if (value) addHint(value);
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2700
2715
|
const fieldPattern = `([^\\n:=?.!,]{1,60}?)`;
|
|
2701
2716
|
const valuePattern = `([^\\n?.!,]{1,120}?)`;
|
|
2702
2717
|
const fieldValuePatterns = [
|
|
@@ -2706,11 +2721,37 @@ function extractQueryFieldHints(question) {
|
|
|
2706
2721
|
];
|
|
2707
2722
|
for (const pattern of fieldValuePatterns) {
|
|
2708
2723
|
for (const match of question.matchAll(pattern)) {
|
|
2709
|
-
|
|
2724
|
+
const field = normalizeHintValue((_b = match[1]) != null ? _b : "");
|
|
2725
|
+
const value = (_c = match[2]) != null ? _c : "";
|
|
2726
|
+
if (field && !isLikelyPromptPhrase(field)) {
|
|
2727
|
+
addHint(value, field);
|
|
2728
|
+
} else {
|
|
2729
|
+
addHint(value);
|
|
2730
|
+
}
|
|
2710
2731
|
}
|
|
2711
2732
|
}
|
|
2733
|
+
for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){1,3}\b/g)) {
|
|
2734
|
+
addHint(match[0]);
|
|
2735
|
+
}
|
|
2712
2736
|
return [...hints.values()];
|
|
2713
2737
|
}
|
|
2738
|
+
function buildQueryFilter(question, hints) {
|
|
2739
|
+
const filter = { metadata: {}, keywords: [], queryText: question };
|
|
2740
|
+
for (const hint of hints) {
|
|
2741
|
+
if (hint.field) {
|
|
2742
|
+
filter.metadata[hint.field] = hint.value;
|
|
2743
|
+
} else {
|
|
2744
|
+
filter.keywords.push(hint.value);
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g)) {
|
|
2748
|
+
const term = normalizeHintValue(match[0]);
|
|
2749
|
+
if (term && !filter.keywords.includes(term)) filter.keywords.push(term);
|
|
2750
|
+
}
|
|
2751
|
+
if (Object.keys(filter.metadata || {}).length === 0) delete filter.metadata;
|
|
2752
|
+
if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
|
|
2753
|
+
return filter;
|
|
2754
|
+
}
|
|
2714
2755
|
var Pipeline = class {
|
|
2715
2756
|
constructor(config) {
|
|
2716
2757
|
this.initialised = false;
|
|
@@ -2799,10 +2840,9 @@ var Pipeline = class {
|
|
|
2799
2840
|
try {
|
|
2800
2841
|
const queryVector = await this.embeddingProvider.embed(question, { taskType: "query" });
|
|
2801
2842
|
const fieldHints = extractQueryFieldHints(question);
|
|
2802
|
-
const
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
});
|
|
2843
|
+
const filter = buildQueryFilter(question, fieldHints);
|
|
2844
|
+
filter.__fieldHints = fieldHints;
|
|
2845
|
+
const rawMatches = await this.vectorDB.query(queryVector, topK, ns, filter);
|
|
2806
2846
|
const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
|
|
2807
2847
|
const context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
|
|
2808
2848
|
${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
package/dist/handlers/index.mjs
CHANGED
package/dist/server.js
CHANGED
|
@@ -2769,7 +2769,11 @@ var EmbeddingStrategyResolver = class {
|
|
|
2769
2769
|
function normalizeHintValue(value) {
|
|
2770
2770
|
return value.replace(/\s+/g, " ").trim();
|
|
2771
2771
|
}
|
|
2772
|
+
function isLikelyPromptPhrase(value) {
|
|
2773
|
+
return /^(what|which|who|where|when|why|how)\b/i.test(value.trim());
|
|
2774
|
+
}
|
|
2772
2775
|
function extractQueryFieldHints(question) {
|
|
2776
|
+
var _a, _b, _c;
|
|
2773
2777
|
if (!question.trim()) return [];
|
|
2774
2778
|
const hints = /* @__PURE__ */ new Map();
|
|
2775
2779
|
const addHint = (value, field) => {
|
|
@@ -2786,6 +2790,17 @@ function extractQueryFieldHints(question) {
|
|
|
2786
2790
|
for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
|
|
2787
2791
|
addHint(match[1]);
|
|
2788
2792
|
}
|
|
2793
|
+
const naturalQuestionPatterns = [
|
|
2794
|
+
/\b(?:what|which)\s+(?:is|are|was|were)\s+(?:the\s+)?([^?.!,]{1,60}?)\s+of\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
|
|
2795
|
+
/\b(?:who|what)\s+(?:is|are|was|were)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
|
|
2796
|
+
/\b(?:about|for|regarding)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi
|
|
2797
|
+
];
|
|
2798
|
+
for (const pattern of naturalQuestionPatterns) {
|
|
2799
|
+
for (const match of question.matchAll(pattern)) {
|
|
2800
|
+
const value = (_a = match[2]) != null ? _a : match[1];
|
|
2801
|
+
if (value) addHint(value);
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2789
2804
|
const fieldPattern = `([^\\n:=?.!,]{1,60}?)`;
|
|
2790
2805
|
const valuePattern = `([^\\n?.!,]{1,120}?)`;
|
|
2791
2806
|
const fieldValuePatterns = [
|
|
@@ -2795,11 +2810,37 @@ function extractQueryFieldHints(question) {
|
|
|
2795
2810
|
];
|
|
2796
2811
|
for (const pattern of fieldValuePatterns) {
|
|
2797
2812
|
for (const match of question.matchAll(pattern)) {
|
|
2798
|
-
|
|
2813
|
+
const field = normalizeHintValue((_b = match[1]) != null ? _b : "");
|
|
2814
|
+
const value = (_c = match[2]) != null ? _c : "";
|
|
2815
|
+
if (field && !isLikelyPromptPhrase(field)) {
|
|
2816
|
+
addHint(value, field);
|
|
2817
|
+
} else {
|
|
2818
|
+
addHint(value);
|
|
2819
|
+
}
|
|
2799
2820
|
}
|
|
2800
2821
|
}
|
|
2822
|
+
for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){1,3}\b/g)) {
|
|
2823
|
+
addHint(match[0]);
|
|
2824
|
+
}
|
|
2801
2825
|
return [...hints.values()];
|
|
2802
2826
|
}
|
|
2827
|
+
function buildQueryFilter(question, hints) {
|
|
2828
|
+
const filter = { metadata: {}, keywords: [], queryText: question };
|
|
2829
|
+
for (const hint of hints) {
|
|
2830
|
+
if (hint.field) {
|
|
2831
|
+
filter.metadata[hint.field] = hint.value;
|
|
2832
|
+
} else {
|
|
2833
|
+
filter.keywords.push(hint.value);
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g)) {
|
|
2837
|
+
const term = normalizeHintValue(match[0]);
|
|
2838
|
+
if (term && !filter.keywords.includes(term)) filter.keywords.push(term);
|
|
2839
|
+
}
|
|
2840
|
+
if (Object.keys(filter.metadata || {}).length === 0) delete filter.metadata;
|
|
2841
|
+
if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
|
|
2842
|
+
return filter;
|
|
2843
|
+
}
|
|
2803
2844
|
var Pipeline = class {
|
|
2804
2845
|
constructor(config) {
|
|
2805
2846
|
this.initialised = false;
|
|
@@ -2888,10 +2929,9 @@ var Pipeline = class {
|
|
|
2888
2929
|
try {
|
|
2889
2930
|
const queryVector = await this.embeddingProvider.embed(question, { taskType: "query" });
|
|
2890
2931
|
const fieldHints = extractQueryFieldHints(question);
|
|
2891
|
-
const
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
});
|
|
2932
|
+
const filter = buildQueryFilter(question, fieldHints);
|
|
2933
|
+
filter.__fieldHints = fieldHints;
|
|
2934
|
+
const rawMatches = await this.vectorDB.query(queryVector, topK, ns, filter);
|
|
2895
2935
|
const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
|
|
2896
2936
|
const context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
|
|
2897
2937
|
${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
package/dist/server.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retrivora-ai/rag-engine",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
|
|
5
5
|
"author": "Abhinav Alkuchi",
|
|
6
6
|
"license": "MIT",
|
package/src/core/Pipeline.ts
CHANGED
|
@@ -12,10 +12,20 @@ interface QueryFieldHint {
|
|
|
12
12
|
value: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
interface QueryFilter {
|
|
16
|
+
metadata?: Record<string, string>;
|
|
17
|
+
keywords?: string[];
|
|
18
|
+
queryText?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
function normalizeHintValue(value: string): string {
|
|
16
22
|
return value.replace(/\s+/g, ' ').trim();
|
|
17
23
|
}
|
|
18
24
|
|
|
25
|
+
function isLikelyPromptPhrase(value: string): boolean {
|
|
26
|
+
return /^(what|which|who|where|when|why|how)\b/i.test(value.trim());
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
function extractQueryFieldHints(question: string): QueryFieldHint[] {
|
|
20
30
|
if (!question.trim()) return [];
|
|
21
31
|
|
|
@@ -45,6 +55,19 @@ function extractQueryFieldHints(question: string): QueryFieldHint[] {
|
|
|
45
55
|
addHint(match[1]);
|
|
46
56
|
}
|
|
47
57
|
|
|
58
|
+
const naturalQuestionPatterns = [
|
|
59
|
+
/\b(?:what|which)\s+(?:is|are|was|were)\s+(?:the\s+)?([^?.!,]{1,60}?)\s+of\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
|
|
60
|
+
/\b(?:who|what)\s+(?:is|are|was|were)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
|
|
61
|
+
/\b(?:about|for|regarding)\s+["']?([^"'\n?.!,]{2,120})["']?(?=[?.!,]|$)/gi,
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
for (const pattern of naturalQuestionPatterns) {
|
|
65
|
+
for (const match of question.matchAll(pattern)) {
|
|
66
|
+
const value = match[2] ?? match[1];
|
|
67
|
+
if (value) addHint(value);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
48
71
|
const fieldPattern = `([^\\n:=?.!,]{1,60}?)`;
|
|
49
72
|
const valuePattern = `([^\\n?.!,]{1,120}?)`;
|
|
50
73
|
const fieldValuePatterns = [
|
|
@@ -55,13 +78,50 @@ function extractQueryFieldHints(question: string): QueryFieldHint[] {
|
|
|
55
78
|
|
|
56
79
|
for (const pattern of fieldValuePatterns) {
|
|
57
80
|
for (const match of question.matchAll(pattern)) {
|
|
58
|
-
|
|
81
|
+
const field = normalizeHintValue(match[1] ?? '');
|
|
82
|
+
const value = match[2] ?? '';
|
|
83
|
+
|
|
84
|
+
if (field && !isLikelyPromptPhrase(field)) {
|
|
85
|
+
addHint(value, field);
|
|
86
|
+
} else {
|
|
87
|
+
addHint(value);
|
|
88
|
+
}
|
|
59
89
|
}
|
|
60
90
|
}
|
|
61
91
|
|
|
92
|
+
for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){1,3}\b/g)) {
|
|
93
|
+
addHint(match[0]);
|
|
94
|
+
}
|
|
95
|
+
|
|
62
96
|
return [...hints.values()];
|
|
63
97
|
}
|
|
64
98
|
|
|
99
|
+
function buildQueryFilter(question: string, hints: QueryFieldHint[]): QueryFilter {
|
|
100
|
+
const filter: QueryFilter = { metadata: {}, keywords: [], queryText: question };
|
|
101
|
+
|
|
102
|
+
for (const hint of hints) {
|
|
103
|
+
if (hint.field) {
|
|
104
|
+
// prefer last-seen value for a field; providers may interpret filters differently
|
|
105
|
+
filter.metadata![hint.field] = hint.value;
|
|
106
|
+
} else {
|
|
107
|
+
// treat as keyword / named entity
|
|
108
|
+
filter.keywords!.push(hint.value);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Also extract quoted phrases (if not already captured) as keywords
|
|
113
|
+
for (const match of question.matchAll(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g)) {
|
|
114
|
+
const term = normalizeHintValue(match[0]);
|
|
115
|
+
if (term && !filter.keywords!.includes(term)) filter.keywords!.push(term);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Remove empty metadata object when no fields present to keep filter minimal
|
|
119
|
+
if (Object.keys(filter.metadata || {}).length === 0) delete filter.metadata;
|
|
120
|
+
if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
|
|
121
|
+
|
|
122
|
+
return filter;
|
|
123
|
+
}
|
|
124
|
+
|
|
65
125
|
/**
|
|
66
126
|
* Pipeline — orchestrates the RAG flow: Embed → Search → Augment → Generate.
|
|
67
127
|
*
|
|
@@ -191,11 +251,15 @@ export class Pipeline {
|
|
|
191
251
|
try {
|
|
192
252
|
const queryVector = await this.embeddingProvider.embed(question, { taskType: 'query' });
|
|
193
253
|
const fieldHints = extractQueryFieldHints(question);
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
254
|
+
// Build a provider-agnostic filter from hints + original query text. Providers
|
|
255
|
+
// may choose to use `queryText` for hybrid keyword matching and `metadata`
|
|
256
|
+
// for exact/filtered metadata queries.
|
|
257
|
+
const filter = buildQueryFilter(question, fieldHints) as Record<string, unknown>;
|
|
258
|
+
|
|
259
|
+
// Keep the raw hints available as well for providers that want richer parsing
|
|
260
|
+
filter.__fieldHints = fieldHints;
|
|
261
|
+
|
|
262
|
+
const rawMatches = await this.vectorDB.query(queryVector, topK, ns, filter);
|
|
199
263
|
|
|
200
264
|
const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
|
|
201
265
|
const context = sources.length
|