@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/handlers/index.js
CHANGED
|
@@ -3907,7 +3907,7 @@ var UniversalLLMAdapter = class {
|
|
|
3907
3907
|
});
|
|
3908
3908
|
}
|
|
3909
3909
|
async chat(messages, context) {
|
|
3910
|
-
var _a2, _b;
|
|
3910
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
3911
3911
|
const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
|
|
3912
3912
|
const formattedMessages = [
|
|
3913
3913
|
{ role: "system", content: `${this.systemPrompt}
|
|
@@ -3932,8 +3932,27 @@ ${context != null ? context : "None"}` },
|
|
|
3932
3932
|
temperature: this.temperature
|
|
3933
3933
|
};
|
|
3934
3934
|
}
|
|
3935
|
+
const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
|
|
3936
|
+
if (isSelfHost || Boolean(process.env.VERCEL)) {
|
|
3937
|
+
const _g2 = globalThis;
|
|
3938
|
+
if (typeof _g2.__retrivoraDispatchChat === "function") {
|
|
3939
|
+
try {
|
|
3940
|
+
const res = await _g2.__retrivoraDispatchChat({
|
|
3941
|
+
model: this.model,
|
|
3942
|
+
messages: formattedMessages,
|
|
3943
|
+
max_tokens: this.maxTokens,
|
|
3944
|
+
temperature: this.temperature
|
|
3945
|
+
}, this.apiKey);
|
|
3946
|
+
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) {
|
|
3947
|
+
return res.response.choices[0].message.content;
|
|
3948
|
+
}
|
|
3949
|
+
} catch (dispatchErr) {
|
|
3950
|
+
throw dispatchErr;
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
}
|
|
3935
3954
|
const { data } = await this.http.post(path2, payload);
|
|
3936
|
-
const extractPath = (
|
|
3955
|
+
const extractPath = (_f = this.opts.responseExtractPath) != null ? _f : "choices[0].message.content";
|
|
3937
3956
|
const result = resolvePath(data, extractPath);
|
|
3938
3957
|
if (result === void 0) {
|
|
3939
3958
|
throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
|
|
@@ -3947,7 +3966,7 @@ ${context != null ? context : "None"}` },
|
|
|
3947
3966
|
*/
|
|
3948
3967
|
chatStream(messages, context) {
|
|
3949
3968
|
return __asyncGenerator(this, null, function* () {
|
|
3950
|
-
var _a2, _b, _c;
|
|
3969
|
+
var _a2, _b, _c, _d, _e, _f, _g2;
|
|
3951
3970
|
const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
|
|
3952
3971
|
const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
|
|
3953
3972
|
const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
|
|
@@ -3978,19 +3997,46 @@ ${context != null ? context : "None"}` },
|
|
|
3978
3997
|
stream: true
|
|
3979
3998
|
};
|
|
3980
3999
|
}
|
|
3981
|
-
const
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
4000
|
+
const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
|
|
4001
|
+
let streamBody = null;
|
|
4002
|
+
if (isSelfHost || Boolean(process.env.VERCEL)) {
|
|
4003
|
+
const _g3 = globalThis;
|
|
4004
|
+
if (typeof _g3.__retrivoraDispatchChat === "function") {
|
|
4005
|
+
try {
|
|
4006
|
+
const res = yield new __await(_g3.__retrivoraDispatchChat({
|
|
4007
|
+
model: this.model,
|
|
4008
|
+
messages: formattedMessages,
|
|
4009
|
+
max_tokens: this.maxTokens,
|
|
4010
|
+
temperature: this.temperature,
|
|
4011
|
+
stream: true
|
|
4012
|
+
}, this.apiKey));
|
|
4013
|
+
if (res == null ? void 0 : res.stream) {
|
|
4014
|
+
streamBody = res.stream;
|
|
4015
|
+
} 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) {
|
|
4016
|
+
yield res.response.choices[0].message.content;
|
|
4017
|
+
return;
|
|
4018
|
+
}
|
|
4019
|
+
} catch (dispatchErr) {
|
|
4020
|
+
throw dispatchErr;
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
3989
4023
|
}
|
|
3990
|
-
if (!
|
|
3991
|
-
|
|
4024
|
+
if (!streamBody) {
|
|
4025
|
+
const response = yield new __await(fetch(url, {
|
|
4026
|
+
method: "POST",
|
|
4027
|
+
headers: this.resolvedHeaders,
|
|
4028
|
+
body: JSON.stringify(payload)
|
|
4029
|
+
}));
|
|
4030
|
+
if (!response.ok) {
|
|
4031
|
+
const errorText = yield new __await(response.text().catch(() => response.statusText));
|
|
4032
|
+
throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
|
|
4033
|
+
}
|
|
4034
|
+
if (!response.body) {
|
|
4035
|
+
throw new Error("[UniversalLLMAdapter] Response body is null \u2014 server did not send a streaming response.");
|
|
4036
|
+
}
|
|
4037
|
+
streamBody = response.body;
|
|
3992
4038
|
}
|
|
3993
|
-
const reader =
|
|
4039
|
+
const reader = streamBody.getReader();
|
|
3994
4040
|
const decoder = new TextDecoder("utf-8");
|
|
3995
4041
|
let buffer = "";
|
|
3996
4042
|
try {
|
|
@@ -3999,7 +4045,7 @@ ${context != null ? context : "None"}` },
|
|
|
3999
4045
|
if (done) break;
|
|
4000
4046
|
buffer += decoder.decode(value, { stream: true });
|
|
4001
4047
|
const lines = buffer.split("\n");
|
|
4002
|
-
buffer = (
|
|
4048
|
+
buffer = (_g2 = lines.pop()) != null ? _g2 : "";
|
|
4003
4049
|
for (const line of lines) {
|
|
4004
4050
|
const trimmed = line.trim();
|
|
4005
4051
|
if (!trimmed || trimmed === "data: [DONE]") continue;
|
|
@@ -4027,7 +4073,7 @@ ${context != null ? context : "None"}` },
|
|
|
4027
4073
|
});
|
|
4028
4074
|
}
|
|
4029
4075
|
async embed(text) {
|
|
4030
|
-
var _a2, _b;
|
|
4076
|
+
var _a2, _b, _c, _d;
|
|
4031
4077
|
const path2 = (_a2 = this.opts.embedPath) != null ? _a2 : "/embeddings";
|
|
4032
4078
|
let payload;
|
|
4033
4079
|
if (this.opts.embedPayloadTemplate) {
|
|
@@ -4041,8 +4087,25 @@ ${context != null ? context : "None"}` },
|
|
|
4041
4087
|
input: text
|
|
4042
4088
|
};
|
|
4043
4089
|
}
|
|
4090
|
+
const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
|
|
4091
|
+
if (isSelfHost || Boolean(process.env.VERCEL)) {
|
|
4092
|
+
const _g2 = globalThis;
|
|
4093
|
+
if (typeof _g2.__retrivoraDispatchEmbedding === "function") {
|
|
4094
|
+
try {
|
|
4095
|
+
const res = await _g2.__retrivoraDispatchEmbedding({
|
|
4096
|
+
model: this.model,
|
|
4097
|
+
input: text
|
|
4098
|
+
}, this.apiKey);
|
|
4099
|
+
if ((_c = (_b = res == null ? void 0 : res.data) == null ? void 0 : _b[0]) == null ? void 0 : _c.embedding) {
|
|
4100
|
+
return res.data[0].embedding;
|
|
4101
|
+
}
|
|
4102
|
+
} catch (dispatchErr) {
|
|
4103
|
+
throw dispatchErr;
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4044
4107
|
const { data } = await this.http.post(path2, payload);
|
|
4045
|
-
const extractPath = (
|
|
4108
|
+
const extractPath = (_d = this.opts.embedExtractPath) != null ? _d : "data[0].embedding";
|
|
4046
4109
|
const vector = resolvePath(data, extractPath);
|
|
4047
4110
|
if (!Array.isArray(vector)) {
|
|
4048
4111
|
throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
|
|
@@ -4631,7 +4694,8 @@ var Reranker = class {
|
|
|
4631
4694
|
return matches.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
4632
4695
|
}
|
|
4633
4696
|
const scoredMatches = matches.map((match) => {
|
|
4634
|
-
|
|
4697
|
+
var _a2;
|
|
4698
|
+
const contentLower = ((_a2 = match == null ? void 0 : match.content) != null ? _a2 : "").toLowerCase();
|
|
4635
4699
|
let keywordScore = 0;
|
|
4636
4700
|
keywords.forEach((keyword) => {
|
|
4637
4701
|
if (contentLower.includes(keyword)) {
|
|
@@ -4652,7 +4716,10 @@ var Reranker = class {
|
|
|
4652
4716
|
[{ role: "user", content: `Query: "${query}"
|
|
4653
4717
|
|
|
4654
4718
|
Documents:
|
|
4655
|
-
${topN.map((m, i) =>
|
|
4719
|
+
${topN.map((m, i) => {
|
|
4720
|
+
var _a2;
|
|
4721
|
+
return `[${i}] ${((_a2 = m == null ? void 0 : m.content) != null ? _a2 : "").replace(/\n/g, " ")}`;
|
|
4722
|
+
}).join("\n")}` }],
|
|
4656
4723
|
"",
|
|
4657
4724
|
{
|
|
4658
4725
|
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.',
|
|
@@ -5817,10 +5884,10 @@ Return ONLY 'vector', 'graph', or 'both'. No explanation.`;
|
|
|
5817
5884
|
"",
|
|
5818
5885
|
{ temperature: 0, maxTokens: 10 }
|
|
5819
5886
|
);
|
|
5820
|
-
const cleanResponse = response.trim().toLowerCase();
|
|
5821
|
-
if (cleanResponse.includes("vector")) return "vector";
|
|
5822
|
-
if (cleanResponse.includes("graph")) return "graph";
|
|
5887
|
+
const cleanResponse = (response != null ? response : "").trim().toLowerCase();
|
|
5823
5888
|
if (cleanResponse.includes("both")) return "both";
|
|
5889
|
+
if (cleanResponse.includes("graph")) return "graph";
|
|
5890
|
+
if (cleanResponse.includes("vector")) return "vector";
|
|
5824
5891
|
} catch (error) {
|
|
5825
5892
|
console.warn("[QueryProcessor] LLM strategy classification failed, falling back to keywords:", error);
|
|
5826
5893
|
}
|
|
@@ -5994,7 +6061,10 @@ var IntentClassifier = class {
|
|
|
5994
6061
|
hasCategoricalFields = catKeys.size > 0;
|
|
5995
6062
|
numericFieldCount = numericKeys.size;
|
|
5996
6063
|
categoricalFieldCount = catKeys.size;
|
|
5997
|
-
if (productKeyMatches >= 2 || docs.some((d) =>
|
|
6064
|
+
if (productKeyMatches >= 2 || docs.some((d) => {
|
|
6065
|
+
var _a2, _b;
|
|
6066
|
+
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");
|
|
6067
|
+
})) {
|
|
5998
6068
|
isProductLike = true;
|
|
5999
6069
|
}
|
|
6000
6070
|
}
|
|
@@ -6274,7 +6344,10 @@ var TextRendererStrategy = class {
|
|
|
6274
6344
|
return { type: "text", title, data: { content: data } };
|
|
6275
6345
|
}
|
|
6276
6346
|
const docs = Array.isArray(data) ? data : [];
|
|
6277
|
-
const text = docs.map((d) =>
|
|
6347
|
+
const text = docs.map((d) => {
|
|
6348
|
+
var _a2;
|
|
6349
|
+
return (_a2 = d == null ? void 0 : d.content) != null ? _a2 : "";
|
|
6350
|
+
}).join("\n\n");
|
|
6278
6351
|
return { type: "text", title, data: { content: text || "No detailed content available." } };
|
|
6279
6352
|
}
|
|
6280
6353
|
};
|
|
@@ -6523,6 +6596,19 @@ var UITransformer = class _UITransformer {
|
|
|
6523
6596
|
if (!retrievedData || retrievedData.length === 0) {
|
|
6524
6597
|
return this.createTextResponse("No data available", "No relevant data found for your query.");
|
|
6525
6598
|
}
|
|
6599
|
+
console.log("[UITransformer.transform] Processing retrievedData:", {
|
|
6600
|
+
userQuery,
|
|
6601
|
+
retrievedCount: retrievedData.length,
|
|
6602
|
+
sample: retrievedData.slice(0, 2).map((item) => {
|
|
6603
|
+
var _a3;
|
|
6604
|
+
return {
|
|
6605
|
+
id: item == null ? void 0 : item.id,
|
|
6606
|
+
contentType: typeof (item == null ? void 0 : item.content),
|
|
6607
|
+
contentSnippet: String((_a3 = item == null ? void 0 : item.content) != null ? _a3 : "").substring(0, 150),
|
|
6608
|
+
metadataKeys: Object.keys((item == null ? void 0 : item.metadata) || {})
|
|
6609
|
+
};
|
|
6610
|
+
})
|
|
6611
|
+
});
|
|
6526
6612
|
const resolvedIntent = intent != null ? intent : this.detectIntentHeuristic(userQuery);
|
|
6527
6613
|
const filteredData = resolvedIntent.filterInStockOnly ? retrievedData.filter((item) => this.determineStockStatus(item)) : retrievedData;
|
|
6528
6614
|
const profile = this.profileData(filteredData);
|
|
@@ -7012,7 +7098,10 @@ ${schemaProfileText}` : ""}`;
|
|
|
7012
7098
|
type: "radar_chart",
|
|
7013
7099
|
title: "Product Comparison",
|
|
7014
7100
|
description: `Comparing ${data.length} items across ${radarData.length} attributes`,
|
|
7015
|
-
data: radarData.length > 0 ? radarData : data.map((d) =>
|
|
7101
|
+
data: radarData.length > 0 ? radarData : data.map((d) => {
|
|
7102
|
+
var _a2;
|
|
7103
|
+
return { attribute: ((_a2 = d == null ? void 0 : d.content) != null ? _a2 : "").substring(0, 40) };
|
|
7104
|
+
})
|
|
7016
7105
|
};
|
|
7017
7106
|
}
|
|
7018
7107
|
static transformToTable(data, query = "") {
|
|
@@ -7029,7 +7118,10 @@ ${schemaProfileText}` : ""}`;
|
|
|
7029
7118
|
static transformToText(data) {
|
|
7030
7119
|
return this.createTextResponse(
|
|
7031
7120
|
"Retrieved Context",
|
|
7032
|
-
data.map((item) =>
|
|
7121
|
+
data.map((item) => {
|
|
7122
|
+
var _a2;
|
|
7123
|
+
return (_a2 = item == null ? void 0 : item.content) != null ? _a2 : "";
|
|
7124
|
+
}).join("\n\n"),
|
|
7033
7125
|
`Found ${data.length} relevant results`
|
|
7034
7126
|
);
|
|
7035
7127
|
}
|
|
@@ -7190,6 +7282,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
7190
7282
|
return productTerms && productAction && !nonProductEntityTerms;
|
|
7191
7283
|
}
|
|
7192
7284
|
static isProductData(item) {
|
|
7285
|
+
if (!item) return false;
|
|
7193
7286
|
const content = (item.content || "").toLowerCase();
|
|
7194
7287
|
const productKeywords = [
|
|
7195
7288
|
"product",
|
|
@@ -7448,12 +7541,12 @@ ${schemaProfileText}` : ""}`;
|
|
|
7448
7541
|
}
|
|
7449
7542
|
static extractTimeSeriesData(data) {
|
|
7450
7543
|
return data.map((item) => {
|
|
7451
|
-
var _a2, _b, _c, _d, _e;
|
|
7544
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
7452
7545
|
const meta = item.metadata || {};
|
|
7453
7546
|
return {
|
|
7454
7547
|
timestamp: (_b = (_a2 = meta.timestamp) != null ? _a2 : meta.date) != null ? _b : (/* @__PURE__ */ new Date()).toISOString(),
|
|
7455
7548
|
value: (_d = (_c = meta.value) != null ? _c : item.score) != null ? _d : 0,
|
|
7456
|
-
label: (
|
|
7549
|
+
label: (_f = meta.label) != null ? _f : ((_e = item == null ? void 0 : item.content) != null ? _e : "").substring(0, 50)
|
|
7457
7550
|
};
|
|
7458
7551
|
});
|
|
7459
7552
|
}
|
|
@@ -7567,7 +7660,8 @@ ${schemaProfileText}` : ""}`;
|
|
|
7567
7660
|
}, query.includes(normalizedField) ? 3 : 0);
|
|
7568
7661
|
}
|
|
7569
7662
|
static resolveTableCellValue(item, column) {
|
|
7570
|
-
|
|
7663
|
+
var _a2, _b, _c;
|
|
7664
|
+
if (column === "Content") return ((_a2 = item == null ? void 0 : item.content) != null ? _a2 : "").substring(0, 100);
|
|
7571
7665
|
const meta = item.metadata || {};
|
|
7572
7666
|
const normalizedColumn = this.normalizeComparableField(column);
|
|
7573
7667
|
const exactMetadata = Object.entries(meta).find(
|
|
@@ -7578,9 +7672,9 @@ ${schemaProfileText}` : ""}`;
|
|
|
7578
7672
|
}
|
|
7579
7673
|
const aliasValue = this.resolveAliasedTableCell(meta, column);
|
|
7580
7674
|
if (aliasValue !== void 0) return this.toDisplayValue(aliasValue);
|
|
7581
|
-
const contentValue = this.extractContentFieldValue(item.content, column);
|
|
7675
|
+
const contentValue = this.extractContentFieldValue((_b = item == null ? void 0 : item.content) != null ? _b : "", column);
|
|
7582
7676
|
if (contentValue !== null) return contentValue;
|
|
7583
|
-
const aliasContentValue = this.extractAliasedContentFieldValue(item.content, column);
|
|
7677
|
+
const aliasContentValue = this.extractAliasedContentFieldValue((_c = item == null ? void 0 : item.content) != null ? _c : "", column);
|
|
7584
7678
|
return aliasContentValue != null ? aliasContentValue : "";
|
|
7585
7679
|
}
|
|
7586
7680
|
static resolveAliasedTableCell(meta, column) {
|
|
@@ -7630,6 +7724,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
7630
7724
|
return { inStockCount: inStock, outOfStockCount: outOfStock };
|
|
7631
7725
|
}
|
|
7632
7726
|
static determineStockStatus(item) {
|
|
7727
|
+
if (!item) return true;
|
|
7633
7728
|
const meta = item.metadata || {};
|
|
7634
7729
|
const stockValue = resolveMetadataValue(meta, "stock");
|
|
7635
7730
|
if (stockValue !== void 0) {
|
|
@@ -7675,35 +7770,64 @@ ${schemaProfileText}` : ""}`;
|
|
|
7675
7770
|
return resolveMetadataValue(meta, uiKey);
|
|
7676
7771
|
}
|
|
7677
7772
|
static extractProductInfo(item, config, trainedSchema) {
|
|
7678
|
-
var _a2;
|
|
7773
|
+
var _a2, _b;
|
|
7774
|
+
if (!item) return null;
|
|
7679
7775
|
const meta = item.metadata || {};
|
|
7776
|
+
const content = item.content || "";
|
|
7680
7777
|
const name = this.getDynamicVal(meta, "name", config, trainedSchema);
|
|
7681
7778
|
const price = this.getDynamicVal(meta, "price", config, trainedSchema);
|
|
7682
7779
|
const brand = this.getDynamicVal(meta, "brand", config, trainedSchema);
|
|
7683
7780
|
const description = this.cleanProductDescription(
|
|
7684
|
-
(_a2 = this.extractProductDescriptionFromContent(
|
|
7781
|
+
(_a2 = this.extractProductDescriptionFromContent(content)) != null ? _a2 : this.getProductDescriptionValue(meta, config, trainedSchema)
|
|
7685
7782
|
);
|
|
7686
7783
|
if (name || this.isProductData(item)) {
|
|
7687
7784
|
let finalName = name ? String(name) : void 0;
|
|
7785
|
+
if (!finalName && content) {
|
|
7786
|
+
const nameMatch = content.match(/(?:Product|Item|Name):\s*([^\n]+)/i);
|
|
7787
|
+
finalName = nameMatch ? nameMatch[1].trim() : (_b = content.split("\n")[0]) == null ? void 0 : _b.substring(0, 60);
|
|
7788
|
+
}
|
|
7688
7789
|
if (!finalName) {
|
|
7689
|
-
|
|
7690
|
-
|
|
7790
|
+
console.warn(
|
|
7791
|
+
`[UITransformer] Unable to read product name from metadata or content for item ${item.id}. Defaulting to 'Unnamed Product'.`,
|
|
7792
|
+
{ metadataKeys: Object.keys(meta), contentLength: content.length }
|
|
7793
|
+
);
|
|
7691
7794
|
}
|
|
7692
7795
|
let finalPrice = typeof price === "number" || typeof price === "string" ? price : void 0;
|
|
7693
|
-
if (!finalPrice) {
|
|
7694
|
-
const priceMatch =
|
|
7796
|
+
if (!finalPrice && content) {
|
|
7797
|
+
const priceMatch = content.match(/(?:Price|Cost):\s*\$?([\d,.]+)/i) || content.match(/\$\s*([\d,.]+)/);
|
|
7695
7798
|
if (priceMatch) finalPrice = priceMatch[1].replace(/,/g, "");
|
|
7696
7799
|
}
|
|
7800
|
+
if (finalPrice === void 0) {
|
|
7801
|
+
console.warn(
|
|
7802
|
+
`[UITransformer] Unable to read product price from metadata or content for item ${item.id}.`,
|
|
7803
|
+
{ metadataKeys: Object.keys(meta), contentSnippet: content.substring(0, 100) }
|
|
7804
|
+
);
|
|
7805
|
+
}
|
|
7697
7806
|
const imageValue = this.getDynamicVal(meta, "image", config, trainedSchema);
|
|
7807
|
+
if (!imageValue) {
|
|
7808
|
+
console.debug(
|
|
7809
|
+
`[UITransformer] Unable to read product image URL from metadata for item ${item.id}.`
|
|
7810
|
+
);
|
|
7811
|
+
}
|
|
7812
|
+
if (!description) {
|
|
7813
|
+
console.debug(
|
|
7814
|
+
`[UITransformer] Unable to read product description from metadata or content for item ${item.id}.`
|
|
7815
|
+
);
|
|
7816
|
+
}
|
|
7698
7817
|
return {
|
|
7699
7818
|
id: item.id,
|
|
7700
|
-
name: finalName,
|
|
7819
|
+
name: finalName || "Unnamed Product",
|
|
7701
7820
|
price: finalPrice,
|
|
7702
7821
|
image: typeof imageValue === "string" ? imageValue : void 0,
|
|
7703
7822
|
brand: brand ? String(brand) : void 0,
|
|
7704
7823
|
description,
|
|
7705
7824
|
inStock: this.determineStockStatus(item)
|
|
7706
7825
|
};
|
|
7826
|
+
} else {
|
|
7827
|
+
console.warn(
|
|
7828
|
+
`[UITransformer] Item ${item.id} skipped: unable to read name or identify as product data from metadata/content.`,
|
|
7829
|
+
{ metadataKeys: Object.keys(meta), contentSnippet: content.substring(0, 100) }
|
|
7830
|
+
);
|
|
7707
7831
|
}
|
|
7708
7832
|
return null;
|
|
7709
7833
|
}
|
|
@@ -7788,13 +7912,13 @@ RULES:
|
|
|
7788
7912
|
5. If no relevant data is found, return text: "I cannot answer this question based on the available information."`;
|
|
7789
7913
|
}
|
|
7790
7914
|
static buildContextSummary(sources, maxChars = 6e3) {
|
|
7791
|
-
const items = sources.map((s, i) => {
|
|
7915
|
+
const items = (sources || []).filter(Boolean).map((s, i) => {
|
|
7792
7916
|
var _a2, _b, _c, _d;
|
|
7793
7917
|
return {
|
|
7794
7918
|
index: i + 1,
|
|
7795
|
-
content: (_b = (_a2 = s.content) == null ? void 0 : _a2.substring(0, 400)) != null ? _b : "",
|
|
7796
|
-
metadata: (_c = s.metadata) != null ? _c : {},
|
|
7797
|
-
score: (_d = s.score) != null ? _d : 0
|
|
7919
|
+
content: (_b = (_a2 = s == null ? void 0 : s.content) == null ? void 0 : _a2.substring(0, 400)) != null ? _b : "",
|
|
7920
|
+
metadata: (_c = s == null ? void 0 : s.metadata) != null ? _c : {},
|
|
7921
|
+
score: (_d = s == null ? void 0 : s.score) != null ? _d : 0
|
|
7798
7922
|
};
|
|
7799
7923
|
});
|
|
7800
7924
|
const full = JSON.stringify(items, null, 2);
|
|
@@ -8363,35 +8487,74 @@ ${m.content}`).join("\n\n---\n\n");
|
|
|
8363
8487
|
const hasNumericPredicates = Array.isArray(filter.__numericPredicates) && filter.__numericPredicates.length > 0;
|
|
8364
8488
|
const wantsExhaustiveList = hasNumericPredicates || hasMetadataFilter || /\b(list|all|show|provide|give|get|browse|find|search|display)\b/i.test(question);
|
|
8365
8489
|
const retrievalLimit = wantsExhaustiveList ? Math.max(topK * 20, 100) : topK * 2;
|
|
8366
|
-
const rawSources = (strategyResult === "vector" || strategyResult === "both") && queryVector && queryVector.length > 0 ? yield new __await(this.vectorDB.query(queryVector, retrievalLimit, ns, filter)) : [];
|
|
8490
|
+
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"));
|
|
8491
|
+
console.log("[Pipeline] Raw vectorDB.query response:", {
|
|
8492
|
+
rawCount: rawSources.length,
|
|
8493
|
+
sample: rawSources.slice(0, 2).map((s) => {
|
|
8494
|
+
var _a3;
|
|
8495
|
+
return {
|
|
8496
|
+
id: s == null ? void 0 : s.id,
|
|
8497
|
+
score: s == null ? void 0 : s.score,
|
|
8498
|
+
contentType: typeof (s == null ? void 0 : s.content),
|
|
8499
|
+
contentSnippet: String((_a3 = s == null ? void 0 : s.content) != null ? _a3 : "").substring(0, 150),
|
|
8500
|
+
metadataKeys: Object.keys((s == null ? void 0 : s.metadata) || {})
|
|
8501
|
+
};
|
|
8502
|
+
})
|
|
8503
|
+
});
|
|
8367
8504
|
const retrieveEnd = performance.now();
|
|
8368
8505
|
const embedMs = retrieveEnd - embedStart;
|
|
8369
8506
|
const retrieveMs = retrieveEnd - embedStart;
|
|
8370
8507
|
const rerankStart = performance.now();
|
|
8371
|
-
const structuredSources = this.applyStructuredFilters(rawSources, filter);
|
|
8372
|
-
let fullSources = hasNumericPredicates ? structuredSources : structuredSources.filter((m) =>
|
|
8508
|
+
const structuredSources = this.applyStructuredFilters(rawSources, filter).filter((s) => Boolean(s && typeof s === "object"));
|
|
8509
|
+
let fullSources = hasNumericPredicates ? structuredSources : structuredSources.filter((m) => {
|
|
8510
|
+
var _a3;
|
|
8511
|
+
return ((_a3 = m == null ? void 0 : m.score) != null ? _a3 : 0) >= scoreThreshold;
|
|
8512
|
+
});
|
|
8373
8513
|
const rerankLimit = wantsExhaustiveList ? retrievalLimit : topK;
|
|
8374
8514
|
const useReranking = arch !== "naive" && (arch === "retrieve-and-rerank" || ((_m = this.config.rag) == null ? void 0 : _m.useReranking));
|
|
8375
8515
|
if (!hasNumericPredicates && useReranking) {
|
|
8376
|
-
fullSources = yield new __await(this.reranker.rerank(fullSources, question, rerankLimit));
|
|
8516
|
+
fullSources = (yield new __await(this.reranker.rerank(fullSources, question, rerankLimit))).filter((s) => Boolean(s && typeof s === "object"));
|
|
8377
8517
|
} else if (!wantsExhaustiveList) {
|
|
8378
8518
|
fullSources = fullSources.slice(0, topK);
|
|
8379
8519
|
}
|
|
8380
8520
|
const rerankMs = performance.now() - rerankStart;
|
|
8381
|
-
let context = fullSources.length ? fullSources.map((m, i) =>
|
|
8382
|
-
|
|
8521
|
+
let context = fullSources.length ? fullSources.map((m, i) => {
|
|
8522
|
+
var _a3;
|
|
8523
|
+
return `[Source ${i + 1}]
|
|
8524
|
+
${(_a3 = m == null ? void 0 : m.content) != null ? _a3 : ""}`;
|
|
8525
|
+
}).join("\n\n---\n\n") : "No relevant context found.";
|
|
8383
8526
|
let displayCount = 15;
|
|
8384
8527
|
if (hasMetadataFilter) {
|
|
8385
8528
|
displayCount = fullSources.length;
|
|
8386
8529
|
} else {
|
|
8387
8530
|
const baseThreshold = Math.max(scoreThreshold, 0.4);
|
|
8388
|
-
const highlyRelevant = fullSources.filter((m) =>
|
|
8531
|
+
const highlyRelevant = fullSources.filter((m) => {
|
|
8532
|
+
var _a3;
|
|
8533
|
+
return ((_a3 = m == null ? void 0 : m.score) != null ? _a3 : 0) >= baseThreshold;
|
|
8534
|
+
});
|
|
8389
8535
|
displayCount = Math.max(highlyRelevant.length, topK);
|
|
8390
8536
|
if (displayCount > 15) {
|
|
8391
8537
|
displayCount = 15;
|
|
8392
8538
|
}
|
|
8393
8539
|
}
|
|
8394
|
-
const sources = [...fullSources].
|
|
8540
|
+
const sources = [...fullSources].filter((s) => Boolean(s && typeof s === "object")).sort((a, b) => {
|
|
8541
|
+
var _a3, _b2;
|
|
8542
|
+
return ((_a3 = b == null ? void 0 : b.score) != null ? _a3 : 0) - ((_b2 = a == null ? void 0 : a.score) != null ? _b2 : 0);
|
|
8543
|
+
}).slice(0, displayCount);
|
|
8544
|
+
console.log("[Pipeline] Final sources for prompt & UI:", {
|
|
8545
|
+
count: sources.length,
|
|
8546
|
+
sources: sources.map((s, idx) => {
|
|
8547
|
+
var _a3;
|
|
8548
|
+
return {
|
|
8549
|
+
rank: idx + 1,
|
|
8550
|
+
id: s == null ? void 0 : s.id,
|
|
8551
|
+
score: s == null ? void 0 : s.score,
|
|
8552
|
+
contentType: typeof (s == null ? void 0 : s.content),
|
|
8553
|
+
contentSnippet: String((_a3 = s == null ? void 0 : s.content) != null ? _a3 : "").substring(0, 150),
|
|
8554
|
+
metadata: s == null ? void 0 : s.metadata
|
|
8555
|
+
};
|
|
8556
|
+
})
|
|
8557
|
+
});
|
|
8395
8558
|
if (graphData && graphData.nodes.length > 0) {
|
|
8396
8559
|
const graphContext = graphData.nodes.map(
|
|
8397
8560
|
(n) => `Entity: ${n.label} (${n.id})${n.properties ? " - " + JSON.stringify(n.properties) : ""}`
|
|
@@ -8417,18 +8580,18 @@ ${context}`;
|
|
|
8417
8580
|
}
|
|
8418
8581
|
yield {
|
|
8419
8582
|
reply: "",
|
|
8420
|
-
sources: sources.map((s) => {
|
|
8421
|
-
var _a3;
|
|
8583
|
+
sources: (sources || []).filter((s) => Boolean(s && typeof s === "object")).map((s) => {
|
|
8584
|
+
var _a3, _b2, _c2;
|
|
8422
8585
|
return {
|
|
8423
8586
|
id: s.id,
|
|
8424
|
-
score: s.score,
|
|
8425
|
-
content: s.content,
|
|
8426
|
-
metadata: (
|
|
8587
|
+
score: (_a3 = s.score) != null ? _a3 : 0,
|
|
8588
|
+
content: (_b2 = s == null ? void 0 : s.content) != null ? _b2 : "",
|
|
8589
|
+
metadata: (_c2 = s == null ? void 0 : s.metadata) != null ? _c2 : {},
|
|
8427
8590
|
namespace: ns
|
|
8428
8591
|
};
|
|
8429
8592
|
})
|
|
8430
8593
|
};
|
|
8431
|
-
const allMetadataKeys = Array.from(new Set(sources.flatMap((s) => Object.keys(s.metadata || {}))));
|
|
8594
|
+
const allMetadataKeys = Array.from(new Set(sources.filter(Boolean).flatMap((s) => Object.keys((s == null ? void 0 : s.metadata) || {}))));
|
|
8432
8595
|
const cachedSchema = allMetadataKeys.length > 0 ? SchemaMapper.getCached(ns, allMetadataKeys) : void 0;
|
|
8433
8596
|
if (allMetadataKeys.length > 0 && !cachedSchema) {
|
|
8434
8597
|
SchemaMapper.train(this.llmProvider, ns, allMetadataKeys).catch(() => {
|
|
@@ -8463,7 +8626,10 @@ ${context}`;
|
|
|
8463
8626
|
if (isSimulatedThinking) {
|
|
8464
8627
|
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.`)";
|
|
8465
8628
|
}
|
|
8466
|
-
const hardenedHistory = [
|
|
8629
|
+
const hardenedHistory = (Array.isArray(history) ? history : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
|
|
8630
|
+
role: m.role || "user",
|
|
8631
|
+
content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
|
|
8632
|
+
}));
|
|
8467
8633
|
const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
|
|
8468
8634
|
const messages = [...hardenedHistory, userQuestion];
|
|
8469
8635
|
const systemPrompt = (_v = this.config.llm.systemPrompt) != null ? _v : "";
|
|
@@ -8610,13 +8776,13 @@ ${context}`;
|
|
|
8610
8776
|
rewrittenQuery,
|
|
8611
8777
|
systemPrompt,
|
|
8612
8778
|
userPrompt: question + finalRestrictionSuffix,
|
|
8613
|
-
chunks: sources.map((s) => {
|
|
8614
|
-
var _a3;
|
|
8779
|
+
chunks: (sources || []).filter(Boolean).map((s) => {
|
|
8780
|
+
var _a3, _b2;
|
|
8615
8781
|
return {
|
|
8616
8782
|
id: s.id,
|
|
8617
8783
|
score: s.score,
|
|
8618
|
-
content: s.content,
|
|
8619
|
-
metadata: (
|
|
8784
|
+
content: (_a3 = s == null ? void 0 : s.content) != null ? _a3 : "",
|
|
8785
|
+
metadata: (_b2 = s == null ? void 0 : s.metadata) != null ? _b2 : {},
|
|
8620
8786
|
namespace: ns
|
|
8621
8787
|
};
|
|
8622
8788
|
}),
|
|
@@ -8717,6 +8883,8 @@ ${context}`;
|
|
|
8717
8883
|
});
|
|
8718
8884
|
}
|
|
8719
8885
|
resolveNumericPredicateValue(source, predicate) {
|
|
8886
|
+
var _a2;
|
|
8887
|
+
if (!source) return null;
|
|
8720
8888
|
const meta = source.metadata || {};
|
|
8721
8889
|
const field = predicate.field;
|
|
8722
8890
|
const entries = Object.entries(meta).filter(([, value]) => value !== null && value !== void 0 && typeof value !== "object");
|
|
@@ -8728,7 +8896,7 @@ ${context}`;
|
|
|
8728
8896
|
const value = this.toFiniteNumber(Array.isArray(fuzzy) ? fuzzy[1] : fuzzy.value);
|
|
8729
8897
|
if (value !== null) return value;
|
|
8730
8898
|
}
|
|
8731
|
-
const contentValue = this.extractNumericValueFromContent(source.content, field);
|
|
8899
|
+
const contentValue = this.extractNumericValueFromContent((_a2 = source.content) != null ? _a2 : "", field);
|
|
8732
8900
|
if (contentValue !== null) return contentValue;
|
|
8733
8901
|
}
|
|
8734
8902
|
for (const [key, value] of entries) {
|
|
@@ -8739,6 +8907,7 @@ ${context}`;
|
|
|
8739
8907
|
return null;
|
|
8740
8908
|
}
|
|
8741
8909
|
extractNumericValueFromContent(content, field) {
|
|
8910
|
+
if (!content || typeof content !== "string") return null;
|
|
8742
8911
|
const escapedWords = field.split(/\s+|_+|-+/).filter(Boolean).map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
8743
8912
|
if (escapedWords.length === 0) return null;
|
|
8744
8913
|
const pattern = new RegExp(`(?:${escapedWords.join("[\\\\s_:-]*")})\\s*[:\\-\u2013]?\\s*([\\d,]+(?:\\.\\d+)?)`, "i");
|
|
@@ -8824,6 +8993,20 @@ ${context}`;
|
|
|
8824
8993
|
resolvedSources.push(source);
|
|
8825
8994
|
}
|
|
8826
8995
|
}
|
|
8996
|
+
console.log("[Pipeline] retrieve() response:", {
|
|
8997
|
+
query,
|
|
8998
|
+
namespace: ns,
|
|
8999
|
+
count: resolvedSources.length,
|
|
9000
|
+
sample: resolvedSources.slice(0, 2).map((s) => {
|
|
9001
|
+
var _a3;
|
|
9002
|
+
return {
|
|
9003
|
+
id: s == null ? void 0 : s.id,
|
|
9004
|
+
score: s == null ? void 0 : s.score,
|
|
9005
|
+
contentSnippet: String((_a3 = s == null ? void 0 : s.content) != null ? _a3 : "").substring(0, 150),
|
|
9006
|
+
metadata: s == null ? void 0 : s.metadata
|
|
9007
|
+
};
|
|
9008
|
+
})
|
|
9009
|
+
});
|
|
8827
9010
|
return { sources: resolvedSources, graphData };
|
|
8828
9011
|
}
|
|
8829
9012
|
/** Rewrite the user query for better retrieval performance. */
|
|
@@ -9218,6 +9401,7 @@ ${csv.trim()}`);
|
|
|
9218
9401
|
// src/core/DatabaseStorage.ts
|
|
9219
9402
|
var fs = __toESM(require("fs"));
|
|
9220
9403
|
var path = __toESM(require("path"));
|
|
9404
|
+
var os = __toESM(require("os"));
|
|
9221
9405
|
var DatabaseStorage = class {
|
|
9222
9406
|
constructor(config) {
|
|
9223
9407
|
// Dynamic PG Pool
|
|
@@ -9226,7 +9410,8 @@ var DatabaseStorage = class {
|
|
|
9226
9410
|
this.mongoClient = null;
|
|
9227
9411
|
this.mongoDbName = "";
|
|
9228
9412
|
// Filesystem fallback configuration
|
|
9229
|
-
this.
|
|
9413
|
+
this.isServerless = Boolean(process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.LAMBDA_TASK_ROOT);
|
|
9414
|
+
this.fallbackDir = this.isServerless ? path.join(os.tmpdir(), ".retrivora") : path.join(process.cwd(), ".retrivora");
|
|
9230
9415
|
this.historyFile = path.join(this.fallbackDir, "history.json");
|
|
9231
9416
|
this.feedbackFile = path.join(this.fallbackDir, "feedback.json");
|
|
9232
9417
|
var _a2, _b, _c, _d, _e;
|
|
@@ -9356,10 +9541,27 @@ var DatabaseStorage = class {
|
|
|
9356
9541
|
}
|
|
9357
9542
|
// Helper for FS fallback writes
|
|
9358
9543
|
writeLocalFile(filePath, data) {
|
|
9359
|
-
|
|
9360
|
-
|
|
9544
|
+
try {
|
|
9545
|
+
const dir = path.dirname(filePath);
|
|
9546
|
+
if (!fs.existsSync(dir)) {
|
|
9547
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
9548
|
+
}
|
|
9549
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
9550
|
+
} catch (err) {
|
|
9551
|
+
if (err.code === "EROFS") {
|
|
9552
|
+
try {
|
|
9553
|
+
const tmpDir = path.join(os.tmpdir(), ".retrivora");
|
|
9554
|
+
if (!fs.existsSync(tmpDir)) {
|
|
9555
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
9556
|
+
}
|
|
9557
|
+
const tmpFilePath = path.join(tmpDir, path.basename(filePath));
|
|
9558
|
+
fs.writeFileSync(tmpFilePath, JSON.stringify(data, null, 2), "utf-8");
|
|
9559
|
+
} catch (e) {
|
|
9560
|
+
}
|
|
9561
|
+
} else {
|
|
9562
|
+
console.warn(`[DatabaseStorage] Failed to write local fallback file: ${err.message}`);
|
|
9563
|
+
}
|
|
9361
9564
|
}
|
|
9362
|
-
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
9363
9565
|
}
|
|
9364
9566
|
// ─── Message History Operations ──────────────────────────────────────────
|
|
9365
9567
|
async saveMessage(sessionId, message) {
|