@retrivora-ai/rag-engine 2.0.5 → 2.0.7
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/handlers/index.js +268 -66
- package/dist/handlers/index.mjs +268 -66
- package/dist/index.js +79 -22
- package/dist/index.mjs +79 -22
- package/dist/server.js +268 -66
- package/dist/server.mjs +268 -66
- package/package.json +1 -1
- package/src/core/DatabaseStorage.ts +26 -4
- package/src/core/Pipeline.ts +61 -18
- package/src/core/QueryProcessor.ts +3 -3
- package/src/llm/providers/UniversalLLMAdapter.ts +83 -11
- package/src/rag/Reranker.ts +2 -2
- package/src/rendering/IntentClassifier.ts +1 -1
- package/src/rendering/RendererRegistry.ts +1 -1
- package/src/utils/ProductExtractor.ts +1 -1
- package/src/utils/UITransformer.ts +62 -17
package/dist/server.mjs
CHANGED
|
@@ -3911,7 +3911,7 @@ var UniversalLLMAdapter = class {
|
|
|
3911
3911
|
});
|
|
3912
3912
|
}
|
|
3913
3913
|
async chat(messages, context) {
|
|
3914
|
-
var _a2, _b;
|
|
3914
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
3915
3915
|
const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
|
|
3916
3916
|
const formattedMessages = [
|
|
3917
3917
|
{ role: "system", content: `${this.systemPrompt}
|
|
@@ -3936,8 +3936,27 @@ ${context != null ? context : "None"}` },
|
|
|
3936
3936
|
temperature: this.temperature
|
|
3937
3937
|
};
|
|
3938
3938
|
}
|
|
3939
|
+
const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
|
|
3940
|
+
if (isSelfHost || Boolean(process.env.VERCEL)) {
|
|
3941
|
+
const _g2 = globalThis;
|
|
3942
|
+
if (typeof _g2.__retrivoraDispatchChat === "function") {
|
|
3943
|
+
try {
|
|
3944
|
+
const res = await _g2.__retrivoraDispatchChat({
|
|
3945
|
+
model: this.model,
|
|
3946
|
+
messages: formattedMessages,
|
|
3947
|
+
max_tokens: this.maxTokens,
|
|
3948
|
+
temperature: this.temperature
|
|
3949
|
+
}, this.apiKey);
|
|
3950
|
+
if ((_e = (_d = (_c = (_b = res == null ? void 0 : res.response) == null ? void 0 : _b.choices) == null ? void 0 : _c[0]) == null ? void 0 : _d.message) == null ? void 0 : _e.content) {
|
|
3951
|
+
return res.response.choices[0].message.content;
|
|
3952
|
+
}
|
|
3953
|
+
} catch (dispatchErr) {
|
|
3954
|
+
throw dispatchErr;
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3939
3958
|
const { data } = await this.http.post(path2, payload);
|
|
3940
|
-
const extractPath = (
|
|
3959
|
+
const extractPath = (_f = this.opts.responseExtractPath) != null ? _f : "choices[0].message.content";
|
|
3941
3960
|
const result = resolvePath(data, extractPath);
|
|
3942
3961
|
if (result === void 0) {
|
|
3943
3962
|
throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
|
|
@@ -3951,7 +3970,7 @@ ${context != null ? context : "None"}` },
|
|
|
3951
3970
|
*/
|
|
3952
3971
|
chatStream(messages, context) {
|
|
3953
3972
|
return __asyncGenerator(this, null, function* () {
|
|
3954
|
-
var _a2, _b, _c;
|
|
3973
|
+
var _a2, _b, _c, _d, _e, _f, _g2;
|
|
3955
3974
|
const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
|
|
3956
3975
|
const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
|
|
3957
3976
|
const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
|
|
@@ -3982,19 +4001,46 @@ ${context != null ? context : "None"}` },
|
|
|
3982
4001
|
stream: true
|
|
3983
4002
|
};
|
|
3984
4003
|
}
|
|
3985
|
-
const
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
4004
|
+
const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
|
|
4005
|
+
let streamBody = null;
|
|
4006
|
+
if (isSelfHost || Boolean(process.env.VERCEL)) {
|
|
4007
|
+
const _g3 = globalThis;
|
|
4008
|
+
if (typeof _g3.__retrivoraDispatchChat === "function") {
|
|
4009
|
+
try {
|
|
4010
|
+
const res = yield new __await(_g3.__retrivoraDispatchChat({
|
|
4011
|
+
model: this.model,
|
|
4012
|
+
messages: formattedMessages,
|
|
4013
|
+
max_tokens: this.maxTokens,
|
|
4014
|
+
temperature: this.temperature,
|
|
4015
|
+
stream: true
|
|
4016
|
+
}, this.apiKey));
|
|
4017
|
+
if (res == null ? void 0 : res.stream) {
|
|
4018
|
+
streamBody = res.stream;
|
|
4019
|
+
} else if ((_f = (_e = (_d = (_c = res == null ? void 0 : res.response) == null ? void 0 : _c.choices) == null ? void 0 : _d[0]) == null ? void 0 : _e.message) == null ? void 0 : _f.content) {
|
|
4020
|
+
yield res.response.choices[0].message.content;
|
|
4021
|
+
return;
|
|
4022
|
+
}
|
|
4023
|
+
} catch (dispatchErr) {
|
|
4024
|
+
throw dispatchErr;
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
3993
4027
|
}
|
|
3994
|
-
if (!
|
|
3995
|
-
|
|
4028
|
+
if (!streamBody) {
|
|
4029
|
+
const response = yield new __await(fetch(url, {
|
|
4030
|
+
method: "POST",
|
|
4031
|
+
headers: this.resolvedHeaders,
|
|
4032
|
+
body: JSON.stringify(payload)
|
|
4033
|
+
}));
|
|
4034
|
+
if (!response.ok) {
|
|
4035
|
+
const errorText = yield new __await(response.text().catch(() => response.statusText));
|
|
4036
|
+
throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
|
|
4037
|
+
}
|
|
4038
|
+
if (!response.body) {
|
|
4039
|
+
throw new Error("[UniversalLLMAdapter] Response body is null \u2014 server did not send a streaming response.");
|
|
4040
|
+
}
|
|
4041
|
+
streamBody = response.body;
|
|
3996
4042
|
}
|
|
3997
|
-
const reader =
|
|
4043
|
+
const reader = streamBody.getReader();
|
|
3998
4044
|
const decoder = new TextDecoder("utf-8");
|
|
3999
4045
|
let buffer = "";
|
|
4000
4046
|
try {
|
|
@@ -4003,7 +4049,7 @@ ${context != null ? context : "None"}` },
|
|
|
4003
4049
|
if (done) break;
|
|
4004
4050
|
buffer += decoder.decode(value, { stream: true });
|
|
4005
4051
|
const lines = buffer.split("\n");
|
|
4006
|
-
buffer = (
|
|
4052
|
+
buffer = (_g2 = lines.pop()) != null ? _g2 : "";
|
|
4007
4053
|
for (const line of lines) {
|
|
4008
4054
|
const trimmed = line.trim();
|
|
4009
4055
|
if (!trimmed || trimmed === "data: [DONE]") continue;
|
|
@@ -4031,7 +4077,7 @@ ${context != null ? context : "None"}` },
|
|
|
4031
4077
|
});
|
|
4032
4078
|
}
|
|
4033
4079
|
async embed(text) {
|
|
4034
|
-
var _a2, _b;
|
|
4080
|
+
var _a2, _b, _c, _d;
|
|
4035
4081
|
const path2 = (_a2 = this.opts.embedPath) != null ? _a2 : "/embeddings";
|
|
4036
4082
|
let payload;
|
|
4037
4083
|
if (this.opts.embedPayloadTemplate) {
|
|
@@ -4045,8 +4091,25 @@ ${context != null ? context : "None"}` },
|
|
|
4045
4091
|
input: text
|
|
4046
4092
|
};
|
|
4047
4093
|
}
|
|
4094
|
+
const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
|
|
4095
|
+
if (isSelfHost || Boolean(process.env.VERCEL)) {
|
|
4096
|
+
const _g2 = globalThis;
|
|
4097
|
+
if (typeof _g2.__retrivoraDispatchEmbedding === "function") {
|
|
4098
|
+
try {
|
|
4099
|
+
const res = await _g2.__retrivoraDispatchEmbedding({
|
|
4100
|
+
model: this.model,
|
|
4101
|
+
input: text
|
|
4102
|
+
}, this.apiKey);
|
|
4103
|
+
if ((_c = (_b = res == null ? void 0 : res.data) == null ? void 0 : _b[0]) == null ? void 0 : _c.embedding) {
|
|
4104
|
+
return res.data[0].embedding;
|
|
4105
|
+
}
|
|
4106
|
+
} catch (dispatchErr) {
|
|
4107
|
+
throw dispatchErr;
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4048
4111
|
const { data } = await this.http.post(path2, payload);
|
|
4049
|
-
const extractPath = (
|
|
4112
|
+
const extractPath = (_d = this.opts.embedExtractPath) != null ? _d : "data[0].embedding";
|
|
4050
4113
|
const vector = resolvePath(data, extractPath);
|
|
4051
4114
|
if (!Array.isArray(vector)) {
|
|
4052
4115
|
throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
|
|
@@ -4635,7 +4698,8 @@ var Reranker = class {
|
|
|
4635
4698
|
return matches.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
4636
4699
|
}
|
|
4637
4700
|
const scoredMatches = matches.map((match) => {
|
|
4638
|
-
|
|
4701
|
+
var _a2;
|
|
4702
|
+
const contentLower = ((_a2 = match == null ? void 0 : match.content) != null ? _a2 : "").toLowerCase();
|
|
4639
4703
|
let keywordScore = 0;
|
|
4640
4704
|
keywords.forEach((keyword) => {
|
|
4641
4705
|
if (contentLower.includes(keyword)) {
|
|
@@ -4656,7 +4720,10 @@ var Reranker = class {
|
|
|
4656
4720
|
[{ role: "user", content: `Query: "${query}"
|
|
4657
4721
|
|
|
4658
4722
|
Documents:
|
|
4659
|
-
${topN.map((m, i) =>
|
|
4723
|
+
${topN.map((m, i) => {
|
|
4724
|
+
var _a2;
|
|
4725
|
+
return `[${i}] ${((_a2 = m == null ? void 0 : m.content) != null ? _a2 : "").replace(/\n/g, " ")}`;
|
|
4726
|
+
}).join("\n")}` }],
|
|
4660
4727
|
"",
|
|
4661
4728
|
{
|
|
4662
4729
|
systemPrompt: 'You are a relevance ranking expert. Given the user query and a list of retrieved document chunks, rank the chunks by relevance to the query. Return the result as a comma-separated list of document indices in order of relevance, from most relevant to least relevant. Use the indices provided in brackets like [0], [1], etc. Only return the indices (e.g., "2,0,1"), nothing else. Do not include brackets in the output.',
|
|
@@ -5827,10 +5894,10 @@ Return ONLY 'vector', 'graph', or 'both'. No explanation.`;
|
|
|
5827
5894
|
"",
|
|
5828
5895
|
{ temperature: 0, maxTokens: 10 }
|
|
5829
5896
|
);
|
|
5830
|
-
const cleanResponse = response.trim().toLowerCase();
|
|
5831
|
-
if (cleanResponse.includes("vector")) return "vector";
|
|
5832
|
-
if (cleanResponse.includes("graph")) return "graph";
|
|
5897
|
+
const cleanResponse = (response != null ? response : "").trim().toLowerCase();
|
|
5833
5898
|
if (cleanResponse.includes("both")) return "both";
|
|
5899
|
+
if (cleanResponse.includes("graph")) return "graph";
|
|
5900
|
+
if (cleanResponse.includes("vector")) return "vector";
|
|
5834
5901
|
} catch (error) {
|
|
5835
5902
|
console.warn("[QueryProcessor] LLM strategy classification failed, falling back to keywords:", error);
|
|
5836
5903
|
}
|
|
@@ -6004,7 +6071,10 @@ var IntentClassifier = class {
|
|
|
6004
6071
|
hasCategoricalFields = catKeys.size > 0;
|
|
6005
6072
|
numericFieldCount = numericKeys.size;
|
|
6006
6073
|
categoricalFieldCount = catKeys.size;
|
|
6007
|
-
if (productKeyMatches >= 2 || docs.some((d) =>
|
|
6074
|
+
if (productKeyMatches >= 2 || docs.some((d) => {
|
|
6075
|
+
var _a2, _b;
|
|
6076
|
+
return ((_a2 = d == null ? void 0 : d.content) != null ? _a2 : "").toLowerCase().includes("price") || ((_b = d == null ? void 0 : d.content) != null ? _b : "").toLowerCase().includes("brand");
|
|
6077
|
+
})) {
|
|
6008
6078
|
isProductLike = true;
|
|
6009
6079
|
}
|
|
6010
6080
|
}
|
|
@@ -6284,7 +6354,10 @@ var TextRendererStrategy = class {
|
|
|
6284
6354
|
return { type: "text", title, data: { content: data } };
|
|
6285
6355
|
}
|
|
6286
6356
|
const docs = Array.isArray(data) ? data : [];
|
|
6287
|
-
const text = docs.map((d) =>
|
|
6357
|
+
const text = docs.map((d) => {
|
|
6358
|
+
var _a2;
|
|
6359
|
+
return (_a2 = d == null ? void 0 : d.content) != null ? _a2 : "";
|
|
6360
|
+
}).join("\n\n");
|
|
6288
6361
|
return { type: "text", title, data: { content: text || "No detailed content available." } };
|
|
6289
6362
|
}
|
|
6290
6363
|
};
|
|
@@ -6541,6 +6614,19 @@ var UITransformer = class _UITransformer {
|
|
|
6541
6614
|
if (!retrievedData || retrievedData.length === 0) {
|
|
6542
6615
|
return this.createTextResponse("No data available", "No relevant data found for your query.");
|
|
6543
6616
|
}
|
|
6617
|
+
console.log("[UITransformer.transform] Processing retrievedData:", {
|
|
6618
|
+
userQuery,
|
|
6619
|
+
retrievedCount: retrievedData.length,
|
|
6620
|
+
sample: retrievedData.slice(0, 2).map((item) => {
|
|
6621
|
+
var _a3;
|
|
6622
|
+
return {
|
|
6623
|
+
id: item == null ? void 0 : item.id,
|
|
6624
|
+
contentType: typeof (item == null ? void 0 : item.content),
|
|
6625
|
+
contentSnippet: String((_a3 = item == null ? void 0 : item.content) != null ? _a3 : "").substring(0, 150),
|
|
6626
|
+
metadataKeys: Object.keys((item == null ? void 0 : item.metadata) || {})
|
|
6627
|
+
};
|
|
6628
|
+
})
|
|
6629
|
+
});
|
|
6544
6630
|
const resolvedIntent = intent != null ? intent : this.detectIntentHeuristic(userQuery);
|
|
6545
6631
|
const filteredData = resolvedIntent.filterInStockOnly ? retrievedData.filter((item) => this.determineStockStatus(item)) : retrievedData;
|
|
6546
6632
|
const profile = this.profileData(filteredData);
|
|
@@ -7030,7 +7116,10 @@ ${schemaProfileText}` : ""}`;
|
|
|
7030
7116
|
type: "radar_chart",
|
|
7031
7117
|
title: "Product Comparison",
|
|
7032
7118
|
description: `Comparing ${data.length} items across ${radarData.length} attributes`,
|
|
7033
|
-
data: radarData.length > 0 ? radarData : data.map((d) =>
|
|
7119
|
+
data: radarData.length > 0 ? radarData : data.map((d) => {
|
|
7120
|
+
var _a2;
|
|
7121
|
+
return { attribute: ((_a2 = d == null ? void 0 : d.content) != null ? _a2 : "").substring(0, 40) };
|
|
7122
|
+
})
|
|
7034
7123
|
};
|
|
7035
7124
|
}
|
|
7036
7125
|
static transformToTable(data, query = "") {
|
|
@@ -7047,7 +7136,10 @@ ${schemaProfileText}` : ""}`;
|
|
|
7047
7136
|
static transformToText(data) {
|
|
7048
7137
|
return this.createTextResponse(
|
|
7049
7138
|
"Retrieved Context",
|
|
7050
|
-
data.map((item) =>
|
|
7139
|
+
data.map((item) => {
|
|
7140
|
+
var _a2;
|
|
7141
|
+
return (_a2 = item == null ? void 0 : item.content) != null ? _a2 : "";
|
|
7142
|
+
}).join("\n\n"),
|
|
7051
7143
|
`Found ${data.length} relevant results`
|
|
7052
7144
|
);
|
|
7053
7145
|
}
|
|
@@ -7208,6 +7300,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
7208
7300
|
return productTerms && productAction && !nonProductEntityTerms;
|
|
7209
7301
|
}
|
|
7210
7302
|
static isProductData(item) {
|
|
7303
|
+
if (!item) return false;
|
|
7211
7304
|
const content = (item.content || "").toLowerCase();
|
|
7212
7305
|
const productKeywords = [
|
|
7213
7306
|
"product",
|
|
@@ -7466,12 +7559,12 @@ ${schemaProfileText}` : ""}`;
|
|
|
7466
7559
|
}
|
|
7467
7560
|
static extractTimeSeriesData(data) {
|
|
7468
7561
|
return data.map((item) => {
|
|
7469
|
-
var _a2, _b, _c, _d, _e;
|
|
7562
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
7470
7563
|
const meta = item.metadata || {};
|
|
7471
7564
|
return {
|
|
7472
7565
|
timestamp: (_b = (_a2 = meta.timestamp) != null ? _a2 : meta.date) != null ? _b : (/* @__PURE__ */ new Date()).toISOString(),
|
|
7473
7566
|
value: (_d = (_c = meta.value) != null ? _c : item.score) != null ? _d : 0,
|
|
7474
|
-
label: (
|
|
7567
|
+
label: (_f = meta.label) != null ? _f : ((_e = item == null ? void 0 : item.content) != null ? _e : "").substring(0, 50)
|
|
7475
7568
|
};
|
|
7476
7569
|
});
|
|
7477
7570
|
}
|
|
@@ -7585,7 +7678,8 @@ ${schemaProfileText}` : ""}`;
|
|
|
7585
7678
|
}, query.includes(normalizedField) ? 3 : 0);
|
|
7586
7679
|
}
|
|
7587
7680
|
static resolveTableCellValue(item, column) {
|
|
7588
|
-
|
|
7681
|
+
var _a2, _b, _c;
|
|
7682
|
+
if (column === "Content") return ((_a2 = item == null ? void 0 : item.content) != null ? _a2 : "").substring(0, 100);
|
|
7589
7683
|
const meta = item.metadata || {};
|
|
7590
7684
|
const normalizedColumn = this.normalizeComparableField(column);
|
|
7591
7685
|
const exactMetadata = Object.entries(meta).find(
|
|
@@ -7596,9 +7690,9 @@ ${schemaProfileText}` : ""}`;
|
|
|
7596
7690
|
}
|
|
7597
7691
|
const aliasValue = this.resolveAliasedTableCell(meta, column);
|
|
7598
7692
|
if (aliasValue !== void 0) return this.toDisplayValue(aliasValue);
|
|
7599
|
-
const contentValue = this.extractContentFieldValue(item.content, column);
|
|
7693
|
+
const contentValue = this.extractContentFieldValue((_b = item == null ? void 0 : item.content) != null ? _b : "", column);
|
|
7600
7694
|
if (contentValue !== null) return contentValue;
|
|
7601
|
-
const aliasContentValue = this.extractAliasedContentFieldValue(item.content, column);
|
|
7695
|
+
const aliasContentValue = this.extractAliasedContentFieldValue((_c = item == null ? void 0 : item.content) != null ? _c : "", column);
|
|
7602
7696
|
return aliasContentValue != null ? aliasContentValue : "";
|
|
7603
7697
|
}
|
|
7604
7698
|
static resolveAliasedTableCell(meta, column) {
|
|
@@ -7648,6 +7742,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
7648
7742
|
return { inStockCount: inStock, outOfStockCount: outOfStock };
|
|
7649
7743
|
}
|
|
7650
7744
|
static determineStockStatus(item) {
|
|
7745
|
+
if (!item) return true;
|
|
7651
7746
|
const meta = item.metadata || {};
|
|
7652
7747
|
const stockValue = resolveMetadataValue(meta, "stock");
|
|
7653
7748
|
if (stockValue !== void 0) {
|
|
@@ -7693,35 +7788,64 @@ ${schemaProfileText}` : ""}`;
|
|
|
7693
7788
|
return resolveMetadataValue(meta, uiKey);
|
|
7694
7789
|
}
|
|
7695
7790
|
static extractProductInfo(item, config, trainedSchema) {
|
|
7696
|
-
var _a2;
|
|
7791
|
+
var _a2, _b;
|
|
7792
|
+
if (!item) return null;
|
|
7697
7793
|
const meta = item.metadata || {};
|
|
7794
|
+
const content = item.content || "";
|
|
7698
7795
|
const name = this.getDynamicVal(meta, "name", config, trainedSchema);
|
|
7699
7796
|
const price = this.getDynamicVal(meta, "price", config, trainedSchema);
|
|
7700
7797
|
const brand = this.getDynamicVal(meta, "brand", config, trainedSchema);
|
|
7701
7798
|
const description = this.cleanProductDescription(
|
|
7702
|
-
(_a2 = this.extractProductDescriptionFromContent(
|
|
7799
|
+
(_a2 = this.extractProductDescriptionFromContent(content)) != null ? _a2 : this.getProductDescriptionValue(meta, config, trainedSchema)
|
|
7703
7800
|
);
|
|
7704
7801
|
if (name || this.isProductData(item)) {
|
|
7705
7802
|
let finalName = name ? String(name) : void 0;
|
|
7803
|
+
if (!finalName && content) {
|
|
7804
|
+
const nameMatch = content.match(/(?:Product|Item|Name):\s*([^\n]+)/i);
|
|
7805
|
+
finalName = nameMatch ? nameMatch[1].trim() : (_b = content.split("\n")[0]) == null ? void 0 : _b.substring(0, 60);
|
|
7806
|
+
}
|
|
7706
7807
|
if (!finalName) {
|
|
7707
|
-
|
|
7708
|
-
|
|
7808
|
+
console.warn(
|
|
7809
|
+
`[UITransformer] Unable to read product name from metadata or content for item ${item.id}. Defaulting to 'Unnamed Product'.`,
|
|
7810
|
+
{ metadataKeys: Object.keys(meta), contentLength: content.length }
|
|
7811
|
+
);
|
|
7709
7812
|
}
|
|
7710
7813
|
let finalPrice = typeof price === "number" || typeof price === "string" ? price : void 0;
|
|
7711
|
-
if (!finalPrice) {
|
|
7712
|
-
const priceMatch =
|
|
7814
|
+
if (!finalPrice && content) {
|
|
7815
|
+
const priceMatch = content.match(/(?:Price|Cost):\s*\$?([\d,.]+)/i) || content.match(/\$\s*([\d,.]+)/);
|
|
7713
7816
|
if (priceMatch) finalPrice = priceMatch[1].replace(/,/g, "");
|
|
7714
7817
|
}
|
|
7818
|
+
if (finalPrice === void 0) {
|
|
7819
|
+
console.warn(
|
|
7820
|
+
`[UITransformer] Unable to read product price from metadata or content for item ${item.id}.`,
|
|
7821
|
+
{ metadataKeys: Object.keys(meta), contentSnippet: content.substring(0, 100) }
|
|
7822
|
+
);
|
|
7823
|
+
}
|
|
7715
7824
|
const imageValue = this.getDynamicVal(meta, "image", config, trainedSchema);
|
|
7825
|
+
if (!imageValue) {
|
|
7826
|
+
console.debug(
|
|
7827
|
+
`[UITransformer] Unable to read product image URL from metadata for item ${item.id}.`
|
|
7828
|
+
);
|
|
7829
|
+
}
|
|
7830
|
+
if (!description) {
|
|
7831
|
+
console.debug(
|
|
7832
|
+
`[UITransformer] Unable to read product description from metadata or content for item ${item.id}.`
|
|
7833
|
+
);
|
|
7834
|
+
}
|
|
7716
7835
|
return {
|
|
7717
7836
|
id: item.id,
|
|
7718
|
-
name: finalName,
|
|
7837
|
+
name: finalName || "Unnamed Product",
|
|
7719
7838
|
price: finalPrice,
|
|
7720
7839
|
image: typeof imageValue === "string" ? imageValue : void 0,
|
|
7721
7840
|
brand: brand ? String(brand) : void 0,
|
|
7722
7841
|
description,
|
|
7723
7842
|
inStock: this.determineStockStatus(item)
|
|
7724
7843
|
};
|
|
7844
|
+
} else {
|
|
7845
|
+
console.warn(
|
|
7846
|
+
`[UITransformer] Item ${item.id} skipped: unable to read name or identify as product data from metadata/content.`,
|
|
7847
|
+
{ metadataKeys: Object.keys(meta), contentSnippet: content.substring(0, 100) }
|
|
7848
|
+
);
|
|
7725
7849
|
}
|
|
7726
7850
|
return null;
|
|
7727
7851
|
}
|
|
@@ -7806,13 +7930,13 @@ RULES:
|
|
|
7806
7930
|
5. If no relevant data is found, return text: "I cannot answer this question based on the available information."`;
|
|
7807
7931
|
}
|
|
7808
7932
|
static buildContextSummary(sources, maxChars = 6e3) {
|
|
7809
|
-
const items = sources.map((s, i) => {
|
|
7933
|
+
const items = (sources || []).filter(Boolean).map((s, i) => {
|
|
7810
7934
|
var _a2, _b, _c, _d;
|
|
7811
7935
|
return {
|
|
7812
7936
|
index: i + 1,
|
|
7813
|
-
content: (_b = (_a2 = s.content) == null ? void 0 : _a2.substring(0, 400)) != null ? _b : "",
|
|
7814
|
-
metadata: (_c = s.metadata) != null ? _c : {},
|
|
7815
|
-
score: (_d = s.score) != null ? _d : 0
|
|
7937
|
+
content: (_b = (_a2 = s == null ? void 0 : s.content) == null ? void 0 : _a2.substring(0, 400)) != null ? _b : "",
|
|
7938
|
+
metadata: (_c = s == null ? void 0 : s.metadata) != null ? _c : {},
|
|
7939
|
+
score: (_d = s == null ? void 0 : s.score) != null ? _d : 0
|
|
7816
7940
|
};
|
|
7817
7941
|
});
|
|
7818
7942
|
const full = JSON.stringify(items, null, 2);
|
|
@@ -8381,35 +8505,74 @@ ${m.content}`).join("\n\n---\n\n");
|
|
|
8381
8505
|
const hasNumericPredicates = Array.isArray(filter.__numericPredicates) && filter.__numericPredicates.length > 0;
|
|
8382
8506
|
const wantsExhaustiveList = hasNumericPredicates || hasMetadataFilter || /\b(list|all|show|provide|give|get|browse|find|search|display)\b/i.test(question);
|
|
8383
8507
|
const retrievalLimit = wantsExhaustiveList ? Math.max(topK * 20, 100) : topK * 2;
|
|
8384
|
-
const rawSources = (strategyResult === "vector" || strategyResult === "both") && queryVector && queryVector.length > 0 ? yield new __await(this.vectorDB.query(queryVector, retrievalLimit, ns, filter)) : [];
|
|
8508
|
+
const rawSources = ((strategyResult === "vector" || strategyResult === "both") && queryVector && queryVector.length > 0 ? yield new __await(this.vectorDB.query(queryVector, retrievalLimit, ns, filter)) : []).filter((s) => Boolean(s && typeof s === "object"));
|
|
8509
|
+
console.log("[Pipeline] Raw vectorDB.query response:", {
|
|
8510
|
+
rawCount: rawSources.length,
|
|
8511
|
+
sample: rawSources.slice(0, 2).map((s) => {
|
|
8512
|
+
var _a3;
|
|
8513
|
+
return {
|
|
8514
|
+
id: s == null ? void 0 : s.id,
|
|
8515
|
+
score: s == null ? void 0 : s.score,
|
|
8516
|
+
contentType: typeof (s == null ? void 0 : s.content),
|
|
8517
|
+
contentSnippet: String((_a3 = s == null ? void 0 : s.content) != null ? _a3 : "").substring(0, 150),
|
|
8518
|
+
metadataKeys: Object.keys((s == null ? void 0 : s.metadata) || {})
|
|
8519
|
+
};
|
|
8520
|
+
})
|
|
8521
|
+
});
|
|
8385
8522
|
const retrieveEnd = performance.now();
|
|
8386
8523
|
const embedMs = retrieveEnd - embedStart;
|
|
8387
8524
|
const retrieveMs = retrieveEnd - embedStart;
|
|
8388
8525
|
const rerankStart = performance.now();
|
|
8389
|
-
const structuredSources = this.applyStructuredFilters(rawSources, filter);
|
|
8390
|
-
let fullSources = hasNumericPredicates ? structuredSources : structuredSources.filter((m) =>
|
|
8526
|
+
const structuredSources = this.applyStructuredFilters(rawSources, filter).filter((s) => Boolean(s && typeof s === "object"));
|
|
8527
|
+
let fullSources = hasNumericPredicates ? structuredSources : structuredSources.filter((m) => {
|
|
8528
|
+
var _a3;
|
|
8529
|
+
return ((_a3 = m == null ? void 0 : m.score) != null ? _a3 : 0) >= scoreThreshold;
|
|
8530
|
+
});
|
|
8391
8531
|
const rerankLimit = wantsExhaustiveList ? retrievalLimit : topK;
|
|
8392
8532
|
const useReranking = arch !== "naive" && (arch === "retrieve-and-rerank" || ((_m = this.config.rag) == null ? void 0 : _m.useReranking));
|
|
8393
8533
|
if (!hasNumericPredicates && useReranking) {
|
|
8394
|
-
fullSources = yield new __await(this.reranker.rerank(fullSources, question, rerankLimit));
|
|
8534
|
+
fullSources = (yield new __await(this.reranker.rerank(fullSources, question, rerankLimit))).filter((s) => Boolean(s && typeof s === "object"));
|
|
8395
8535
|
} else if (!wantsExhaustiveList) {
|
|
8396
8536
|
fullSources = fullSources.slice(0, topK);
|
|
8397
8537
|
}
|
|
8398
8538
|
const rerankMs = performance.now() - rerankStart;
|
|
8399
|
-
let context = fullSources.length ? fullSources.map((m, i) =>
|
|
8400
|
-
|
|
8539
|
+
let context = fullSources.length ? fullSources.map((m, i) => {
|
|
8540
|
+
var _a3;
|
|
8541
|
+
return `[Source ${i + 1}]
|
|
8542
|
+
${(_a3 = m == null ? void 0 : m.content) != null ? _a3 : ""}`;
|
|
8543
|
+
}).join("\n\n---\n\n") : "No relevant context found.";
|
|
8401
8544
|
let displayCount = 15;
|
|
8402
8545
|
if (hasMetadataFilter) {
|
|
8403
8546
|
displayCount = fullSources.length;
|
|
8404
8547
|
} else {
|
|
8405
8548
|
const baseThreshold = Math.max(scoreThreshold, 0.4);
|
|
8406
|
-
const highlyRelevant = fullSources.filter((m) =>
|
|
8549
|
+
const highlyRelevant = fullSources.filter((m) => {
|
|
8550
|
+
var _a3;
|
|
8551
|
+
return ((_a3 = m == null ? void 0 : m.score) != null ? _a3 : 0) >= baseThreshold;
|
|
8552
|
+
});
|
|
8407
8553
|
displayCount = Math.max(highlyRelevant.length, topK);
|
|
8408
8554
|
if (displayCount > 15) {
|
|
8409
8555
|
displayCount = 15;
|
|
8410
8556
|
}
|
|
8411
8557
|
}
|
|
8412
|
-
const sources = [...fullSources].
|
|
8558
|
+
const sources = [...fullSources].filter((s) => Boolean(s && typeof s === "object")).sort((a, b) => {
|
|
8559
|
+
var _a3, _b2;
|
|
8560
|
+
return ((_a3 = b == null ? void 0 : b.score) != null ? _a3 : 0) - ((_b2 = a == null ? void 0 : a.score) != null ? _b2 : 0);
|
|
8561
|
+
}).slice(0, displayCount);
|
|
8562
|
+
console.log("[Pipeline] Final sources for prompt & UI:", {
|
|
8563
|
+
count: sources.length,
|
|
8564
|
+
sources: sources.map((s, idx) => {
|
|
8565
|
+
var _a3;
|
|
8566
|
+
return {
|
|
8567
|
+
rank: idx + 1,
|
|
8568
|
+
id: s == null ? void 0 : s.id,
|
|
8569
|
+
score: s == null ? void 0 : s.score,
|
|
8570
|
+
contentType: typeof (s == null ? void 0 : s.content),
|
|
8571
|
+
contentSnippet: String((_a3 = s == null ? void 0 : s.content) != null ? _a3 : "").substring(0, 150),
|
|
8572
|
+
metadata: s == null ? void 0 : s.metadata
|
|
8573
|
+
};
|
|
8574
|
+
})
|
|
8575
|
+
});
|
|
8413
8576
|
if (graphData && graphData.nodes.length > 0) {
|
|
8414
8577
|
const graphContext = graphData.nodes.map(
|
|
8415
8578
|
(n) => `Entity: ${n.label} (${n.id})${n.properties ? " - " + JSON.stringify(n.properties) : ""}`
|
|
@@ -8435,18 +8598,18 @@ ${context}`;
|
|
|
8435
8598
|
}
|
|
8436
8599
|
yield {
|
|
8437
8600
|
reply: "",
|
|
8438
|
-
sources: sources.map((s) => {
|
|
8439
|
-
var _a3;
|
|
8601
|
+
sources: (sources || []).filter((s) => Boolean(s && typeof s === "object")).map((s) => {
|
|
8602
|
+
var _a3, _b2, _c2;
|
|
8440
8603
|
return {
|
|
8441
8604
|
id: s.id,
|
|
8442
|
-
score: s.score,
|
|
8443
|
-
content: s.content,
|
|
8444
|
-
metadata: (
|
|
8605
|
+
score: (_a3 = s.score) != null ? _a3 : 0,
|
|
8606
|
+
content: (_b2 = s == null ? void 0 : s.content) != null ? _b2 : "",
|
|
8607
|
+
metadata: (_c2 = s == null ? void 0 : s.metadata) != null ? _c2 : {},
|
|
8445
8608
|
namespace: ns
|
|
8446
8609
|
};
|
|
8447
8610
|
})
|
|
8448
8611
|
};
|
|
8449
|
-
const allMetadataKeys = Array.from(new Set(sources.flatMap((s) => Object.keys(s.metadata || {}))));
|
|
8612
|
+
const allMetadataKeys = Array.from(new Set(sources.filter(Boolean).flatMap((s) => Object.keys((s == null ? void 0 : s.metadata) || {}))));
|
|
8450
8613
|
const cachedSchema = allMetadataKeys.length > 0 ? SchemaMapper.getCached(ns, allMetadataKeys) : void 0;
|
|
8451
8614
|
if (allMetadataKeys.length > 0 && !cachedSchema) {
|
|
8452
8615
|
SchemaMapper.train(this.llmProvider, ns, allMetadataKeys).catch(() => {
|
|
@@ -8481,7 +8644,10 @@ ${context}`;
|
|
|
8481
8644
|
if (isSimulatedThinking) {
|
|
8482
8645
|
finalRestrictionSuffix += "\n\n(IMPORTANT: You must think step-by-step before answering. Write your thinking process inside a `<think>...</think>` block. Write the `<think>` block first, then follow it with your final response. Keep the thinking process thorough and detailed. Example: `<think>Thinking details here...</think>Final answer text here.`)";
|
|
8483
8646
|
}
|
|
8484
|
-
const hardenedHistory = [
|
|
8647
|
+
const hardenedHistory = (Array.isArray(history) ? history : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
|
|
8648
|
+
role: m.role || "user",
|
|
8649
|
+
content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
|
|
8650
|
+
}));
|
|
8485
8651
|
const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
|
|
8486
8652
|
const messages = [...hardenedHistory, userQuestion];
|
|
8487
8653
|
const systemPrompt = (_v = this.config.llm.systemPrompt) != null ? _v : "";
|
|
@@ -8628,13 +8794,13 @@ ${context}`;
|
|
|
8628
8794
|
rewrittenQuery,
|
|
8629
8795
|
systemPrompt,
|
|
8630
8796
|
userPrompt: question + finalRestrictionSuffix,
|
|
8631
|
-
chunks: sources.map((s) => {
|
|
8632
|
-
var _a3;
|
|
8797
|
+
chunks: (sources || []).filter(Boolean).map((s) => {
|
|
8798
|
+
var _a3, _b2;
|
|
8633
8799
|
return {
|
|
8634
8800
|
id: s.id,
|
|
8635
8801
|
score: s.score,
|
|
8636
|
-
content: s.content,
|
|
8637
|
-
metadata: (
|
|
8802
|
+
content: (_a3 = s == null ? void 0 : s.content) != null ? _a3 : "",
|
|
8803
|
+
metadata: (_b2 = s == null ? void 0 : s.metadata) != null ? _b2 : {},
|
|
8638
8804
|
namespace: ns
|
|
8639
8805
|
};
|
|
8640
8806
|
}),
|
|
@@ -8735,6 +8901,8 @@ ${context}`;
|
|
|
8735
8901
|
});
|
|
8736
8902
|
}
|
|
8737
8903
|
resolveNumericPredicateValue(source, predicate) {
|
|
8904
|
+
var _a2;
|
|
8905
|
+
if (!source) return null;
|
|
8738
8906
|
const meta = source.metadata || {};
|
|
8739
8907
|
const field = predicate.field;
|
|
8740
8908
|
const entries = Object.entries(meta).filter(([, value]) => value !== null && value !== void 0 && typeof value !== "object");
|
|
@@ -8746,7 +8914,7 @@ ${context}`;
|
|
|
8746
8914
|
const value = this.toFiniteNumber(Array.isArray(fuzzy) ? fuzzy[1] : fuzzy.value);
|
|
8747
8915
|
if (value !== null) return value;
|
|
8748
8916
|
}
|
|
8749
|
-
const contentValue = this.extractNumericValueFromContent(source.content, field);
|
|
8917
|
+
const contentValue = this.extractNumericValueFromContent((_a2 = source.content) != null ? _a2 : "", field);
|
|
8750
8918
|
if (contentValue !== null) return contentValue;
|
|
8751
8919
|
}
|
|
8752
8920
|
for (const [key, value] of entries) {
|
|
@@ -8757,6 +8925,7 @@ ${context}`;
|
|
|
8757
8925
|
return null;
|
|
8758
8926
|
}
|
|
8759
8927
|
extractNumericValueFromContent(content, field) {
|
|
8928
|
+
if (!content || typeof content !== "string") return null;
|
|
8760
8929
|
const escapedWords = field.split(/\s+|_+|-+/).filter(Boolean).map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
8761
8930
|
if (escapedWords.length === 0) return null;
|
|
8762
8931
|
const pattern = new RegExp(`(?:${escapedWords.join("[\\\\s_:-]*")})\\s*[:\\-\u2013]?\\s*([\\d,]+(?:\\.\\d+)?)`, "i");
|
|
@@ -8842,6 +9011,20 @@ ${context}`;
|
|
|
8842
9011
|
resolvedSources.push(source);
|
|
8843
9012
|
}
|
|
8844
9013
|
}
|
|
9014
|
+
console.log("[Pipeline] retrieve() response:", {
|
|
9015
|
+
query,
|
|
9016
|
+
namespace: ns,
|
|
9017
|
+
count: resolvedSources.length,
|
|
9018
|
+
sample: resolvedSources.slice(0, 2).map((s) => {
|
|
9019
|
+
var _a3;
|
|
9020
|
+
return {
|
|
9021
|
+
id: s == null ? void 0 : s.id,
|
|
9022
|
+
score: s == null ? void 0 : s.score,
|
|
9023
|
+
contentSnippet: String((_a3 = s == null ? void 0 : s.content) != null ? _a3 : "").substring(0, 150),
|
|
9024
|
+
metadata: s == null ? void 0 : s.metadata
|
|
9025
|
+
};
|
|
9026
|
+
})
|
|
9027
|
+
});
|
|
8845
9028
|
return { sources: resolvedSources, graphData };
|
|
8846
9029
|
}
|
|
8847
9030
|
/** Rewrite the user query for better retrieval performance. */
|
|
@@ -9535,6 +9718,7 @@ import { NextResponse } from "next/server";
|
|
|
9535
9718
|
// src/core/DatabaseStorage.ts
|
|
9536
9719
|
import * as fs from "fs";
|
|
9537
9720
|
import * as path from "path";
|
|
9721
|
+
import * as os from "os";
|
|
9538
9722
|
var DatabaseStorage = class {
|
|
9539
9723
|
constructor(config) {
|
|
9540
9724
|
// Dynamic PG Pool
|
|
@@ -9543,7 +9727,8 @@ var DatabaseStorage = class {
|
|
|
9543
9727
|
this.mongoClient = null;
|
|
9544
9728
|
this.mongoDbName = "";
|
|
9545
9729
|
// Filesystem fallback configuration
|
|
9546
|
-
this.
|
|
9730
|
+
this.isServerless = Boolean(process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.LAMBDA_TASK_ROOT);
|
|
9731
|
+
this.fallbackDir = this.isServerless ? path.join(os.tmpdir(), ".retrivora") : path.join(process.cwd(), ".retrivora");
|
|
9547
9732
|
this.historyFile = path.join(this.fallbackDir, "history.json");
|
|
9548
9733
|
this.feedbackFile = path.join(this.fallbackDir, "feedback.json");
|
|
9549
9734
|
var _a2, _b, _c, _d, _e;
|
|
@@ -9673,10 +9858,27 @@ var DatabaseStorage = class {
|
|
|
9673
9858
|
}
|
|
9674
9859
|
// Helper for FS fallback writes
|
|
9675
9860
|
writeLocalFile(filePath, data) {
|
|
9676
|
-
|
|
9677
|
-
|
|
9861
|
+
try {
|
|
9862
|
+
const dir = path.dirname(filePath);
|
|
9863
|
+
if (!fs.existsSync(dir)) {
|
|
9864
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
9865
|
+
}
|
|
9866
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
9867
|
+
} catch (err) {
|
|
9868
|
+
if (err.code === "EROFS") {
|
|
9869
|
+
try {
|
|
9870
|
+
const tmpDir = path.join(os.tmpdir(), ".retrivora");
|
|
9871
|
+
if (!fs.existsSync(tmpDir)) {
|
|
9872
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
9873
|
+
}
|
|
9874
|
+
const tmpFilePath = path.join(tmpDir, path.basename(filePath));
|
|
9875
|
+
fs.writeFileSync(tmpFilePath, JSON.stringify(data, null, 2), "utf-8");
|
|
9876
|
+
} catch (e) {
|
|
9877
|
+
}
|
|
9878
|
+
} else {
|
|
9879
|
+
console.warn(`[DatabaseStorage] Failed to write local fallback file: ${err.message}`);
|
|
9880
|
+
}
|
|
9678
9881
|
}
|
|
9679
|
-
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
9680
9882
|
}
|
|
9681
9883
|
// ─── Message History Operations ──────────────────────────────────────────
|
|
9682
9884
|
async saveMessage(sessionId, message) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retrivora-ai/rag-engine",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.7",
|
|
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": "UNLICENSED",
|