@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.mjs
CHANGED
|
@@ -3872,7 +3872,7 @@ var UniversalLLMAdapter = class {
|
|
|
3872
3872
|
});
|
|
3873
3873
|
}
|
|
3874
3874
|
async chat(messages, context) {
|
|
3875
|
-
var _a2, _b;
|
|
3875
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
3876
3876
|
const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
|
|
3877
3877
|
const formattedMessages = [
|
|
3878
3878
|
{ role: "system", content: `${this.systemPrompt}
|
|
@@ -3897,8 +3897,27 @@ ${context != null ? context : "None"}` },
|
|
|
3897
3897
|
temperature: this.temperature
|
|
3898
3898
|
};
|
|
3899
3899
|
}
|
|
3900
|
+
const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
|
|
3901
|
+
if (isSelfHost || Boolean(process.env.VERCEL)) {
|
|
3902
|
+
const _g2 = globalThis;
|
|
3903
|
+
if (typeof _g2.__retrivoraDispatchChat === "function") {
|
|
3904
|
+
try {
|
|
3905
|
+
const res = await _g2.__retrivoraDispatchChat({
|
|
3906
|
+
model: this.model,
|
|
3907
|
+
messages: formattedMessages,
|
|
3908
|
+
max_tokens: this.maxTokens,
|
|
3909
|
+
temperature: this.temperature
|
|
3910
|
+
}, this.apiKey);
|
|
3911
|
+
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) {
|
|
3912
|
+
return res.response.choices[0].message.content;
|
|
3913
|
+
}
|
|
3914
|
+
} catch (dispatchErr) {
|
|
3915
|
+
throw dispatchErr;
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3900
3919
|
const { data } = await this.http.post(path2, payload);
|
|
3901
|
-
const extractPath = (
|
|
3920
|
+
const extractPath = (_f = this.opts.responseExtractPath) != null ? _f : "choices[0].message.content";
|
|
3902
3921
|
const result = resolvePath(data, extractPath);
|
|
3903
3922
|
if (result === void 0) {
|
|
3904
3923
|
throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
|
|
@@ -3912,7 +3931,7 @@ ${context != null ? context : "None"}` },
|
|
|
3912
3931
|
*/
|
|
3913
3932
|
chatStream(messages, context) {
|
|
3914
3933
|
return __asyncGenerator(this, null, function* () {
|
|
3915
|
-
var _a2, _b, _c;
|
|
3934
|
+
var _a2, _b, _c, _d, _e, _f, _g2;
|
|
3916
3935
|
const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
|
|
3917
3936
|
const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
|
|
3918
3937
|
const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
|
|
@@ -3943,19 +3962,46 @@ ${context != null ? context : "None"}` },
|
|
|
3943
3962
|
stream: true
|
|
3944
3963
|
};
|
|
3945
3964
|
}
|
|
3946
|
-
const
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3965
|
+
const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
|
|
3966
|
+
let streamBody = null;
|
|
3967
|
+
if (isSelfHost || Boolean(process.env.VERCEL)) {
|
|
3968
|
+
const _g3 = globalThis;
|
|
3969
|
+
if (typeof _g3.__retrivoraDispatchChat === "function") {
|
|
3970
|
+
try {
|
|
3971
|
+
const res = yield new __await(_g3.__retrivoraDispatchChat({
|
|
3972
|
+
model: this.model,
|
|
3973
|
+
messages: formattedMessages,
|
|
3974
|
+
max_tokens: this.maxTokens,
|
|
3975
|
+
temperature: this.temperature,
|
|
3976
|
+
stream: true
|
|
3977
|
+
}, this.apiKey));
|
|
3978
|
+
if (res == null ? void 0 : res.stream) {
|
|
3979
|
+
streamBody = res.stream;
|
|
3980
|
+
} 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) {
|
|
3981
|
+
yield res.response.choices[0].message.content;
|
|
3982
|
+
return;
|
|
3983
|
+
}
|
|
3984
|
+
} catch (dispatchErr) {
|
|
3985
|
+
throw dispatchErr;
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3954
3988
|
}
|
|
3955
|
-
if (!
|
|
3956
|
-
|
|
3989
|
+
if (!streamBody) {
|
|
3990
|
+
const response = yield new __await(fetch(url, {
|
|
3991
|
+
method: "POST",
|
|
3992
|
+
headers: this.resolvedHeaders,
|
|
3993
|
+
body: JSON.stringify(payload)
|
|
3994
|
+
}));
|
|
3995
|
+
if (!response.ok) {
|
|
3996
|
+
const errorText = yield new __await(response.text().catch(() => response.statusText));
|
|
3997
|
+
throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
|
|
3998
|
+
}
|
|
3999
|
+
if (!response.body) {
|
|
4000
|
+
throw new Error("[UniversalLLMAdapter] Response body is null \u2014 server did not send a streaming response.");
|
|
4001
|
+
}
|
|
4002
|
+
streamBody = response.body;
|
|
3957
4003
|
}
|
|
3958
|
-
const reader =
|
|
4004
|
+
const reader = streamBody.getReader();
|
|
3959
4005
|
const decoder = new TextDecoder("utf-8");
|
|
3960
4006
|
let buffer = "";
|
|
3961
4007
|
try {
|
|
@@ -3964,7 +4010,7 @@ ${context != null ? context : "None"}` },
|
|
|
3964
4010
|
if (done) break;
|
|
3965
4011
|
buffer += decoder.decode(value, { stream: true });
|
|
3966
4012
|
const lines = buffer.split("\n");
|
|
3967
|
-
buffer = (
|
|
4013
|
+
buffer = (_g2 = lines.pop()) != null ? _g2 : "";
|
|
3968
4014
|
for (const line of lines) {
|
|
3969
4015
|
const trimmed = line.trim();
|
|
3970
4016
|
if (!trimmed || trimmed === "data: [DONE]") continue;
|
|
@@ -3992,7 +4038,7 @@ ${context != null ? context : "None"}` },
|
|
|
3992
4038
|
});
|
|
3993
4039
|
}
|
|
3994
4040
|
async embed(text) {
|
|
3995
|
-
var _a2, _b;
|
|
4041
|
+
var _a2, _b, _c, _d;
|
|
3996
4042
|
const path2 = (_a2 = this.opts.embedPath) != null ? _a2 : "/embeddings";
|
|
3997
4043
|
let payload;
|
|
3998
4044
|
if (this.opts.embedPayloadTemplate) {
|
|
@@ -4006,8 +4052,25 @@ ${context != null ? context : "None"}` },
|
|
|
4006
4052
|
input: text
|
|
4007
4053
|
};
|
|
4008
4054
|
}
|
|
4055
|
+
const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
|
|
4056
|
+
if (isSelfHost || Boolean(process.env.VERCEL)) {
|
|
4057
|
+
const _g2 = globalThis;
|
|
4058
|
+
if (typeof _g2.__retrivoraDispatchEmbedding === "function") {
|
|
4059
|
+
try {
|
|
4060
|
+
const res = await _g2.__retrivoraDispatchEmbedding({
|
|
4061
|
+
model: this.model,
|
|
4062
|
+
input: text
|
|
4063
|
+
}, this.apiKey);
|
|
4064
|
+
if ((_c = (_b = res == null ? void 0 : res.data) == null ? void 0 : _b[0]) == null ? void 0 : _c.embedding) {
|
|
4065
|
+
return res.data[0].embedding;
|
|
4066
|
+
}
|
|
4067
|
+
} catch (dispatchErr) {
|
|
4068
|
+
throw dispatchErr;
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4009
4072
|
const { data } = await this.http.post(path2, payload);
|
|
4010
|
-
const extractPath = (
|
|
4073
|
+
const extractPath = (_d = this.opts.embedExtractPath) != null ? _d : "data[0].embedding";
|
|
4011
4074
|
const vector = resolvePath(data, extractPath);
|
|
4012
4075
|
if (!Array.isArray(vector)) {
|
|
4013
4076
|
throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
|
|
@@ -4596,7 +4659,8 @@ var Reranker = class {
|
|
|
4596
4659
|
return matches.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
4597
4660
|
}
|
|
4598
4661
|
const scoredMatches = matches.map((match) => {
|
|
4599
|
-
|
|
4662
|
+
var _a2;
|
|
4663
|
+
const contentLower = ((_a2 = match == null ? void 0 : match.content) != null ? _a2 : "").toLowerCase();
|
|
4600
4664
|
let keywordScore = 0;
|
|
4601
4665
|
keywords.forEach((keyword) => {
|
|
4602
4666
|
if (contentLower.includes(keyword)) {
|
|
@@ -4617,7 +4681,10 @@ var Reranker = class {
|
|
|
4617
4681
|
[{ role: "user", content: `Query: "${query}"
|
|
4618
4682
|
|
|
4619
4683
|
Documents:
|
|
4620
|
-
${topN.map((m, i) =>
|
|
4684
|
+
${topN.map((m, i) => {
|
|
4685
|
+
var _a2;
|
|
4686
|
+
return `[${i}] ${((_a2 = m == null ? void 0 : m.content) != null ? _a2 : "").replace(/\n/g, " ")}`;
|
|
4687
|
+
}).join("\n")}` }],
|
|
4621
4688
|
"",
|
|
4622
4689
|
{
|
|
4623
4690
|
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.',
|
|
@@ -5782,10 +5849,10 @@ Return ONLY 'vector', 'graph', or 'both'. No explanation.`;
|
|
|
5782
5849
|
"",
|
|
5783
5850
|
{ temperature: 0, maxTokens: 10 }
|
|
5784
5851
|
);
|
|
5785
|
-
const cleanResponse = response.trim().toLowerCase();
|
|
5786
|
-
if (cleanResponse.includes("vector")) return "vector";
|
|
5787
|
-
if (cleanResponse.includes("graph")) return "graph";
|
|
5852
|
+
const cleanResponse = (response != null ? response : "").trim().toLowerCase();
|
|
5788
5853
|
if (cleanResponse.includes("both")) return "both";
|
|
5854
|
+
if (cleanResponse.includes("graph")) return "graph";
|
|
5855
|
+
if (cleanResponse.includes("vector")) return "vector";
|
|
5789
5856
|
} catch (error) {
|
|
5790
5857
|
console.warn("[QueryProcessor] LLM strategy classification failed, falling back to keywords:", error);
|
|
5791
5858
|
}
|
|
@@ -5959,7 +6026,10 @@ var IntentClassifier = class {
|
|
|
5959
6026
|
hasCategoricalFields = catKeys.size > 0;
|
|
5960
6027
|
numericFieldCount = numericKeys.size;
|
|
5961
6028
|
categoricalFieldCount = catKeys.size;
|
|
5962
|
-
if (productKeyMatches >= 2 || docs.some((d) =>
|
|
6029
|
+
if (productKeyMatches >= 2 || docs.some((d) => {
|
|
6030
|
+
var _a2, _b;
|
|
6031
|
+
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");
|
|
6032
|
+
})) {
|
|
5963
6033
|
isProductLike = true;
|
|
5964
6034
|
}
|
|
5965
6035
|
}
|
|
@@ -6239,7 +6309,10 @@ var TextRendererStrategy = class {
|
|
|
6239
6309
|
return { type: "text", title, data: { content: data } };
|
|
6240
6310
|
}
|
|
6241
6311
|
const docs = Array.isArray(data) ? data : [];
|
|
6242
|
-
const text = docs.map((d) =>
|
|
6312
|
+
const text = docs.map((d) => {
|
|
6313
|
+
var _a2;
|
|
6314
|
+
return (_a2 = d == null ? void 0 : d.content) != null ? _a2 : "";
|
|
6315
|
+
}).join("\n\n");
|
|
6243
6316
|
return { type: "text", title, data: { content: text || "No detailed content available." } };
|
|
6244
6317
|
}
|
|
6245
6318
|
};
|
|
@@ -6488,6 +6561,19 @@ var UITransformer = class _UITransformer {
|
|
|
6488
6561
|
if (!retrievedData || retrievedData.length === 0) {
|
|
6489
6562
|
return this.createTextResponse("No data available", "No relevant data found for your query.");
|
|
6490
6563
|
}
|
|
6564
|
+
console.log("[UITransformer.transform] Processing retrievedData:", {
|
|
6565
|
+
userQuery,
|
|
6566
|
+
retrievedCount: retrievedData.length,
|
|
6567
|
+
sample: retrievedData.slice(0, 2).map((item) => {
|
|
6568
|
+
var _a3;
|
|
6569
|
+
return {
|
|
6570
|
+
id: item == null ? void 0 : item.id,
|
|
6571
|
+
contentType: typeof (item == null ? void 0 : item.content),
|
|
6572
|
+
contentSnippet: String((_a3 = item == null ? void 0 : item.content) != null ? _a3 : "").substring(0, 150),
|
|
6573
|
+
metadataKeys: Object.keys((item == null ? void 0 : item.metadata) || {})
|
|
6574
|
+
};
|
|
6575
|
+
})
|
|
6576
|
+
});
|
|
6491
6577
|
const resolvedIntent = intent != null ? intent : this.detectIntentHeuristic(userQuery);
|
|
6492
6578
|
const filteredData = resolvedIntent.filterInStockOnly ? retrievedData.filter((item) => this.determineStockStatus(item)) : retrievedData;
|
|
6493
6579
|
const profile = this.profileData(filteredData);
|
|
@@ -6977,7 +7063,10 @@ ${schemaProfileText}` : ""}`;
|
|
|
6977
7063
|
type: "radar_chart",
|
|
6978
7064
|
title: "Product Comparison",
|
|
6979
7065
|
description: `Comparing ${data.length} items across ${radarData.length} attributes`,
|
|
6980
|
-
data: radarData.length > 0 ? radarData : data.map((d) =>
|
|
7066
|
+
data: radarData.length > 0 ? radarData : data.map((d) => {
|
|
7067
|
+
var _a2;
|
|
7068
|
+
return { attribute: ((_a2 = d == null ? void 0 : d.content) != null ? _a2 : "").substring(0, 40) };
|
|
7069
|
+
})
|
|
6981
7070
|
};
|
|
6982
7071
|
}
|
|
6983
7072
|
static transformToTable(data, query = "") {
|
|
@@ -6994,7 +7083,10 @@ ${schemaProfileText}` : ""}`;
|
|
|
6994
7083
|
static transformToText(data) {
|
|
6995
7084
|
return this.createTextResponse(
|
|
6996
7085
|
"Retrieved Context",
|
|
6997
|
-
data.map((item) =>
|
|
7086
|
+
data.map((item) => {
|
|
7087
|
+
var _a2;
|
|
7088
|
+
return (_a2 = item == null ? void 0 : item.content) != null ? _a2 : "";
|
|
7089
|
+
}).join("\n\n"),
|
|
6998
7090
|
`Found ${data.length} relevant results`
|
|
6999
7091
|
);
|
|
7000
7092
|
}
|
|
@@ -7155,6 +7247,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
7155
7247
|
return productTerms && productAction && !nonProductEntityTerms;
|
|
7156
7248
|
}
|
|
7157
7249
|
static isProductData(item) {
|
|
7250
|
+
if (!item) return false;
|
|
7158
7251
|
const content = (item.content || "").toLowerCase();
|
|
7159
7252
|
const productKeywords = [
|
|
7160
7253
|
"product",
|
|
@@ -7413,12 +7506,12 @@ ${schemaProfileText}` : ""}`;
|
|
|
7413
7506
|
}
|
|
7414
7507
|
static extractTimeSeriesData(data) {
|
|
7415
7508
|
return data.map((item) => {
|
|
7416
|
-
var _a2, _b, _c, _d, _e;
|
|
7509
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
7417
7510
|
const meta = item.metadata || {};
|
|
7418
7511
|
return {
|
|
7419
7512
|
timestamp: (_b = (_a2 = meta.timestamp) != null ? _a2 : meta.date) != null ? _b : (/* @__PURE__ */ new Date()).toISOString(),
|
|
7420
7513
|
value: (_d = (_c = meta.value) != null ? _c : item.score) != null ? _d : 0,
|
|
7421
|
-
label: (
|
|
7514
|
+
label: (_f = meta.label) != null ? _f : ((_e = item == null ? void 0 : item.content) != null ? _e : "").substring(0, 50)
|
|
7422
7515
|
};
|
|
7423
7516
|
});
|
|
7424
7517
|
}
|
|
@@ -7532,7 +7625,8 @@ ${schemaProfileText}` : ""}`;
|
|
|
7532
7625
|
}, query.includes(normalizedField) ? 3 : 0);
|
|
7533
7626
|
}
|
|
7534
7627
|
static resolveTableCellValue(item, column) {
|
|
7535
|
-
|
|
7628
|
+
var _a2, _b, _c;
|
|
7629
|
+
if (column === "Content") return ((_a2 = item == null ? void 0 : item.content) != null ? _a2 : "").substring(0, 100);
|
|
7536
7630
|
const meta = item.metadata || {};
|
|
7537
7631
|
const normalizedColumn = this.normalizeComparableField(column);
|
|
7538
7632
|
const exactMetadata = Object.entries(meta).find(
|
|
@@ -7543,9 +7637,9 @@ ${schemaProfileText}` : ""}`;
|
|
|
7543
7637
|
}
|
|
7544
7638
|
const aliasValue = this.resolveAliasedTableCell(meta, column);
|
|
7545
7639
|
if (aliasValue !== void 0) return this.toDisplayValue(aliasValue);
|
|
7546
|
-
const contentValue = this.extractContentFieldValue(item.content, column);
|
|
7640
|
+
const contentValue = this.extractContentFieldValue((_b = item == null ? void 0 : item.content) != null ? _b : "", column);
|
|
7547
7641
|
if (contentValue !== null) return contentValue;
|
|
7548
|
-
const aliasContentValue = this.extractAliasedContentFieldValue(item.content, column);
|
|
7642
|
+
const aliasContentValue = this.extractAliasedContentFieldValue((_c = item == null ? void 0 : item.content) != null ? _c : "", column);
|
|
7549
7643
|
return aliasContentValue != null ? aliasContentValue : "";
|
|
7550
7644
|
}
|
|
7551
7645
|
static resolveAliasedTableCell(meta, column) {
|
|
@@ -7595,6 +7689,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
7595
7689
|
return { inStockCount: inStock, outOfStockCount: outOfStock };
|
|
7596
7690
|
}
|
|
7597
7691
|
static determineStockStatus(item) {
|
|
7692
|
+
if (!item) return true;
|
|
7598
7693
|
const meta = item.metadata || {};
|
|
7599
7694
|
const stockValue = resolveMetadataValue(meta, "stock");
|
|
7600
7695
|
if (stockValue !== void 0) {
|
|
@@ -7640,35 +7735,64 @@ ${schemaProfileText}` : ""}`;
|
|
|
7640
7735
|
return resolveMetadataValue(meta, uiKey);
|
|
7641
7736
|
}
|
|
7642
7737
|
static extractProductInfo(item, config, trainedSchema) {
|
|
7643
|
-
var _a2;
|
|
7738
|
+
var _a2, _b;
|
|
7739
|
+
if (!item) return null;
|
|
7644
7740
|
const meta = item.metadata || {};
|
|
7741
|
+
const content = item.content || "";
|
|
7645
7742
|
const name = this.getDynamicVal(meta, "name", config, trainedSchema);
|
|
7646
7743
|
const price = this.getDynamicVal(meta, "price", config, trainedSchema);
|
|
7647
7744
|
const brand = this.getDynamicVal(meta, "brand", config, trainedSchema);
|
|
7648
7745
|
const description = this.cleanProductDescription(
|
|
7649
|
-
(_a2 = this.extractProductDescriptionFromContent(
|
|
7746
|
+
(_a2 = this.extractProductDescriptionFromContent(content)) != null ? _a2 : this.getProductDescriptionValue(meta, config, trainedSchema)
|
|
7650
7747
|
);
|
|
7651
7748
|
if (name || this.isProductData(item)) {
|
|
7652
7749
|
let finalName = name ? String(name) : void 0;
|
|
7750
|
+
if (!finalName && content) {
|
|
7751
|
+
const nameMatch = content.match(/(?:Product|Item|Name):\s*([^\n]+)/i);
|
|
7752
|
+
finalName = nameMatch ? nameMatch[1].trim() : (_b = content.split("\n")[0]) == null ? void 0 : _b.substring(0, 60);
|
|
7753
|
+
}
|
|
7653
7754
|
if (!finalName) {
|
|
7654
|
-
|
|
7655
|
-
|
|
7755
|
+
console.warn(
|
|
7756
|
+
`[UITransformer] Unable to read product name from metadata or content for item ${item.id}. Defaulting to 'Unnamed Product'.`,
|
|
7757
|
+
{ metadataKeys: Object.keys(meta), contentLength: content.length }
|
|
7758
|
+
);
|
|
7656
7759
|
}
|
|
7657
7760
|
let finalPrice = typeof price === "number" || typeof price === "string" ? price : void 0;
|
|
7658
|
-
if (!finalPrice) {
|
|
7659
|
-
const priceMatch =
|
|
7761
|
+
if (!finalPrice && content) {
|
|
7762
|
+
const priceMatch = content.match(/(?:Price|Cost):\s*\$?([\d,.]+)/i) || content.match(/\$\s*([\d,.]+)/);
|
|
7660
7763
|
if (priceMatch) finalPrice = priceMatch[1].replace(/,/g, "");
|
|
7661
7764
|
}
|
|
7765
|
+
if (finalPrice === void 0) {
|
|
7766
|
+
console.warn(
|
|
7767
|
+
`[UITransformer] Unable to read product price from metadata or content for item ${item.id}.`,
|
|
7768
|
+
{ metadataKeys: Object.keys(meta), contentSnippet: content.substring(0, 100) }
|
|
7769
|
+
);
|
|
7770
|
+
}
|
|
7662
7771
|
const imageValue = this.getDynamicVal(meta, "image", config, trainedSchema);
|
|
7772
|
+
if (!imageValue) {
|
|
7773
|
+
console.debug(
|
|
7774
|
+
`[UITransformer] Unable to read product image URL from metadata for item ${item.id}.`
|
|
7775
|
+
);
|
|
7776
|
+
}
|
|
7777
|
+
if (!description) {
|
|
7778
|
+
console.debug(
|
|
7779
|
+
`[UITransformer] Unable to read product description from metadata or content for item ${item.id}.`
|
|
7780
|
+
);
|
|
7781
|
+
}
|
|
7663
7782
|
return {
|
|
7664
7783
|
id: item.id,
|
|
7665
|
-
name: finalName,
|
|
7784
|
+
name: finalName || "Unnamed Product",
|
|
7666
7785
|
price: finalPrice,
|
|
7667
7786
|
image: typeof imageValue === "string" ? imageValue : void 0,
|
|
7668
7787
|
brand: brand ? String(brand) : void 0,
|
|
7669
7788
|
description,
|
|
7670
7789
|
inStock: this.determineStockStatus(item)
|
|
7671
7790
|
};
|
|
7791
|
+
} else {
|
|
7792
|
+
console.warn(
|
|
7793
|
+
`[UITransformer] Item ${item.id} skipped: unable to read name or identify as product data from metadata/content.`,
|
|
7794
|
+
{ metadataKeys: Object.keys(meta), contentSnippet: content.substring(0, 100) }
|
|
7795
|
+
);
|
|
7672
7796
|
}
|
|
7673
7797
|
return null;
|
|
7674
7798
|
}
|
|
@@ -7753,13 +7877,13 @@ RULES:
|
|
|
7753
7877
|
5. If no relevant data is found, return text: "I cannot answer this question based on the available information."`;
|
|
7754
7878
|
}
|
|
7755
7879
|
static buildContextSummary(sources, maxChars = 6e3) {
|
|
7756
|
-
const items = sources.map((s, i) => {
|
|
7880
|
+
const items = (sources || []).filter(Boolean).map((s, i) => {
|
|
7757
7881
|
var _a2, _b, _c, _d;
|
|
7758
7882
|
return {
|
|
7759
7883
|
index: i + 1,
|
|
7760
|
-
content: (_b = (_a2 = s.content) == null ? void 0 : _a2.substring(0, 400)) != null ? _b : "",
|
|
7761
|
-
metadata: (_c = s.metadata) != null ? _c : {},
|
|
7762
|
-
score: (_d = s.score) != null ? _d : 0
|
|
7884
|
+
content: (_b = (_a2 = s == null ? void 0 : s.content) == null ? void 0 : _a2.substring(0, 400)) != null ? _b : "",
|
|
7885
|
+
metadata: (_c = s == null ? void 0 : s.metadata) != null ? _c : {},
|
|
7886
|
+
score: (_d = s == null ? void 0 : s.score) != null ? _d : 0
|
|
7763
7887
|
};
|
|
7764
7888
|
});
|
|
7765
7889
|
const full = JSON.stringify(items, null, 2);
|
|
@@ -8328,35 +8452,74 @@ ${m.content}`).join("\n\n---\n\n");
|
|
|
8328
8452
|
const hasNumericPredicates = Array.isArray(filter.__numericPredicates) && filter.__numericPredicates.length > 0;
|
|
8329
8453
|
const wantsExhaustiveList = hasNumericPredicates || hasMetadataFilter || /\b(list|all|show|provide|give|get|browse|find|search|display)\b/i.test(question);
|
|
8330
8454
|
const retrievalLimit = wantsExhaustiveList ? Math.max(topK * 20, 100) : topK * 2;
|
|
8331
|
-
const rawSources = (strategyResult === "vector" || strategyResult === "both") && queryVector && queryVector.length > 0 ? yield new __await(this.vectorDB.query(queryVector, retrievalLimit, ns, filter)) : [];
|
|
8455
|
+
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"));
|
|
8456
|
+
console.log("[Pipeline] Raw vectorDB.query response:", {
|
|
8457
|
+
rawCount: rawSources.length,
|
|
8458
|
+
sample: rawSources.slice(0, 2).map((s) => {
|
|
8459
|
+
var _a3;
|
|
8460
|
+
return {
|
|
8461
|
+
id: s == null ? void 0 : s.id,
|
|
8462
|
+
score: s == null ? void 0 : s.score,
|
|
8463
|
+
contentType: typeof (s == null ? void 0 : s.content),
|
|
8464
|
+
contentSnippet: String((_a3 = s == null ? void 0 : s.content) != null ? _a3 : "").substring(0, 150),
|
|
8465
|
+
metadataKeys: Object.keys((s == null ? void 0 : s.metadata) || {})
|
|
8466
|
+
};
|
|
8467
|
+
})
|
|
8468
|
+
});
|
|
8332
8469
|
const retrieveEnd = performance.now();
|
|
8333
8470
|
const embedMs = retrieveEnd - embedStart;
|
|
8334
8471
|
const retrieveMs = retrieveEnd - embedStart;
|
|
8335
8472
|
const rerankStart = performance.now();
|
|
8336
|
-
const structuredSources = this.applyStructuredFilters(rawSources, filter);
|
|
8337
|
-
let fullSources = hasNumericPredicates ? structuredSources : structuredSources.filter((m) =>
|
|
8473
|
+
const structuredSources = this.applyStructuredFilters(rawSources, filter).filter((s) => Boolean(s && typeof s === "object"));
|
|
8474
|
+
let fullSources = hasNumericPredicates ? structuredSources : structuredSources.filter((m) => {
|
|
8475
|
+
var _a3;
|
|
8476
|
+
return ((_a3 = m == null ? void 0 : m.score) != null ? _a3 : 0) >= scoreThreshold;
|
|
8477
|
+
});
|
|
8338
8478
|
const rerankLimit = wantsExhaustiveList ? retrievalLimit : topK;
|
|
8339
8479
|
const useReranking = arch !== "naive" && (arch === "retrieve-and-rerank" || ((_m = this.config.rag) == null ? void 0 : _m.useReranking));
|
|
8340
8480
|
if (!hasNumericPredicates && useReranking) {
|
|
8341
|
-
fullSources = yield new __await(this.reranker.rerank(fullSources, question, rerankLimit));
|
|
8481
|
+
fullSources = (yield new __await(this.reranker.rerank(fullSources, question, rerankLimit))).filter((s) => Boolean(s && typeof s === "object"));
|
|
8342
8482
|
} else if (!wantsExhaustiveList) {
|
|
8343
8483
|
fullSources = fullSources.slice(0, topK);
|
|
8344
8484
|
}
|
|
8345
8485
|
const rerankMs = performance.now() - rerankStart;
|
|
8346
|
-
let context = fullSources.length ? fullSources.map((m, i) =>
|
|
8347
|
-
|
|
8486
|
+
let context = fullSources.length ? fullSources.map((m, i) => {
|
|
8487
|
+
var _a3;
|
|
8488
|
+
return `[Source ${i + 1}]
|
|
8489
|
+
${(_a3 = m == null ? void 0 : m.content) != null ? _a3 : ""}`;
|
|
8490
|
+
}).join("\n\n---\n\n") : "No relevant context found.";
|
|
8348
8491
|
let displayCount = 15;
|
|
8349
8492
|
if (hasMetadataFilter) {
|
|
8350
8493
|
displayCount = fullSources.length;
|
|
8351
8494
|
} else {
|
|
8352
8495
|
const baseThreshold = Math.max(scoreThreshold, 0.4);
|
|
8353
|
-
const highlyRelevant = fullSources.filter((m) =>
|
|
8496
|
+
const highlyRelevant = fullSources.filter((m) => {
|
|
8497
|
+
var _a3;
|
|
8498
|
+
return ((_a3 = m == null ? void 0 : m.score) != null ? _a3 : 0) >= baseThreshold;
|
|
8499
|
+
});
|
|
8354
8500
|
displayCount = Math.max(highlyRelevant.length, topK);
|
|
8355
8501
|
if (displayCount > 15) {
|
|
8356
8502
|
displayCount = 15;
|
|
8357
8503
|
}
|
|
8358
8504
|
}
|
|
8359
|
-
const sources = [...fullSources].
|
|
8505
|
+
const sources = [...fullSources].filter((s) => Boolean(s && typeof s === "object")).sort((a, b) => {
|
|
8506
|
+
var _a3, _b2;
|
|
8507
|
+
return ((_a3 = b == null ? void 0 : b.score) != null ? _a3 : 0) - ((_b2 = a == null ? void 0 : a.score) != null ? _b2 : 0);
|
|
8508
|
+
}).slice(0, displayCount);
|
|
8509
|
+
console.log("[Pipeline] Final sources for prompt & UI:", {
|
|
8510
|
+
count: sources.length,
|
|
8511
|
+
sources: sources.map((s, idx) => {
|
|
8512
|
+
var _a3;
|
|
8513
|
+
return {
|
|
8514
|
+
rank: idx + 1,
|
|
8515
|
+
id: s == null ? void 0 : s.id,
|
|
8516
|
+
score: s == null ? void 0 : s.score,
|
|
8517
|
+
contentType: typeof (s == null ? void 0 : s.content),
|
|
8518
|
+
contentSnippet: String((_a3 = s == null ? void 0 : s.content) != null ? _a3 : "").substring(0, 150),
|
|
8519
|
+
metadata: s == null ? void 0 : s.metadata
|
|
8520
|
+
};
|
|
8521
|
+
})
|
|
8522
|
+
});
|
|
8360
8523
|
if (graphData && graphData.nodes.length > 0) {
|
|
8361
8524
|
const graphContext = graphData.nodes.map(
|
|
8362
8525
|
(n) => `Entity: ${n.label} (${n.id})${n.properties ? " - " + JSON.stringify(n.properties) : ""}`
|
|
@@ -8382,18 +8545,18 @@ ${context}`;
|
|
|
8382
8545
|
}
|
|
8383
8546
|
yield {
|
|
8384
8547
|
reply: "",
|
|
8385
|
-
sources: sources.map((s) => {
|
|
8386
|
-
var _a3;
|
|
8548
|
+
sources: (sources || []).filter((s) => Boolean(s && typeof s === "object")).map((s) => {
|
|
8549
|
+
var _a3, _b2, _c2;
|
|
8387
8550
|
return {
|
|
8388
8551
|
id: s.id,
|
|
8389
|
-
score: s.score,
|
|
8390
|
-
content: s.content,
|
|
8391
|
-
metadata: (
|
|
8552
|
+
score: (_a3 = s.score) != null ? _a3 : 0,
|
|
8553
|
+
content: (_b2 = s == null ? void 0 : s.content) != null ? _b2 : "",
|
|
8554
|
+
metadata: (_c2 = s == null ? void 0 : s.metadata) != null ? _c2 : {},
|
|
8392
8555
|
namespace: ns
|
|
8393
8556
|
};
|
|
8394
8557
|
})
|
|
8395
8558
|
};
|
|
8396
|
-
const allMetadataKeys = Array.from(new Set(sources.flatMap((s) => Object.keys(s.metadata || {}))));
|
|
8559
|
+
const allMetadataKeys = Array.from(new Set(sources.filter(Boolean).flatMap((s) => Object.keys((s == null ? void 0 : s.metadata) || {}))));
|
|
8397
8560
|
const cachedSchema = allMetadataKeys.length > 0 ? SchemaMapper.getCached(ns, allMetadataKeys) : void 0;
|
|
8398
8561
|
if (allMetadataKeys.length > 0 && !cachedSchema) {
|
|
8399
8562
|
SchemaMapper.train(this.llmProvider, ns, allMetadataKeys).catch(() => {
|
|
@@ -8428,7 +8591,10 @@ ${context}`;
|
|
|
8428
8591
|
if (isSimulatedThinking) {
|
|
8429
8592
|
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.`)";
|
|
8430
8593
|
}
|
|
8431
|
-
const hardenedHistory = [
|
|
8594
|
+
const hardenedHistory = (Array.isArray(history) ? history : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
|
|
8595
|
+
role: m.role || "user",
|
|
8596
|
+
content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
|
|
8597
|
+
}));
|
|
8432
8598
|
const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
|
|
8433
8599
|
const messages = [...hardenedHistory, userQuestion];
|
|
8434
8600
|
const systemPrompt = (_v = this.config.llm.systemPrompt) != null ? _v : "";
|
|
@@ -8575,13 +8741,13 @@ ${context}`;
|
|
|
8575
8741
|
rewrittenQuery,
|
|
8576
8742
|
systemPrompt,
|
|
8577
8743
|
userPrompt: question + finalRestrictionSuffix,
|
|
8578
|
-
chunks: sources.map((s) => {
|
|
8579
|
-
var _a3;
|
|
8744
|
+
chunks: (sources || []).filter(Boolean).map((s) => {
|
|
8745
|
+
var _a3, _b2;
|
|
8580
8746
|
return {
|
|
8581
8747
|
id: s.id,
|
|
8582
8748
|
score: s.score,
|
|
8583
|
-
content: s.content,
|
|
8584
|
-
metadata: (
|
|
8749
|
+
content: (_a3 = s == null ? void 0 : s.content) != null ? _a3 : "",
|
|
8750
|
+
metadata: (_b2 = s == null ? void 0 : s.metadata) != null ? _b2 : {},
|
|
8585
8751
|
namespace: ns
|
|
8586
8752
|
};
|
|
8587
8753
|
}),
|
|
@@ -8682,6 +8848,8 @@ ${context}`;
|
|
|
8682
8848
|
});
|
|
8683
8849
|
}
|
|
8684
8850
|
resolveNumericPredicateValue(source, predicate) {
|
|
8851
|
+
var _a2;
|
|
8852
|
+
if (!source) return null;
|
|
8685
8853
|
const meta = source.metadata || {};
|
|
8686
8854
|
const field = predicate.field;
|
|
8687
8855
|
const entries = Object.entries(meta).filter(([, value]) => value !== null && value !== void 0 && typeof value !== "object");
|
|
@@ -8693,7 +8861,7 @@ ${context}`;
|
|
|
8693
8861
|
const value = this.toFiniteNumber(Array.isArray(fuzzy) ? fuzzy[1] : fuzzy.value);
|
|
8694
8862
|
if (value !== null) return value;
|
|
8695
8863
|
}
|
|
8696
|
-
const contentValue = this.extractNumericValueFromContent(source.content, field);
|
|
8864
|
+
const contentValue = this.extractNumericValueFromContent((_a2 = source.content) != null ? _a2 : "", field);
|
|
8697
8865
|
if (contentValue !== null) return contentValue;
|
|
8698
8866
|
}
|
|
8699
8867
|
for (const [key, value] of entries) {
|
|
@@ -8704,6 +8872,7 @@ ${context}`;
|
|
|
8704
8872
|
return null;
|
|
8705
8873
|
}
|
|
8706
8874
|
extractNumericValueFromContent(content, field) {
|
|
8875
|
+
if (!content || typeof content !== "string") return null;
|
|
8707
8876
|
const escapedWords = field.split(/\s+|_+|-+/).filter(Boolean).map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
8708
8877
|
if (escapedWords.length === 0) return null;
|
|
8709
8878
|
const pattern = new RegExp(`(?:${escapedWords.join("[\\\\s_:-]*")})\\s*[:\\-\u2013]?\\s*([\\d,]+(?:\\.\\d+)?)`, "i");
|
|
@@ -8789,6 +8958,20 @@ ${context}`;
|
|
|
8789
8958
|
resolvedSources.push(source);
|
|
8790
8959
|
}
|
|
8791
8960
|
}
|
|
8961
|
+
console.log("[Pipeline] retrieve() response:", {
|
|
8962
|
+
query,
|
|
8963
|
+
namespace: ns,
|
|
8964
|
+
count: resolvedSources.length,
|
|
8965
|
+
sample: resolvedSources.slice(0, 2).map((s) => {
|
|
8966
|
+
var _a3;
|
|
8967
|
+
return {
|
|
8968
|
+
id: s == null ? void 0 : s.id,
|
|
8969
|
+
score: s == null ? void 0 : s.score,
|
|
8970
|
+
contentSnippet: String((_a3 = s == null ? void 0 : s.content) != null ? _a3 : "").substring(0, 150),
|
|
8971
|
+
metadata: s == null ? void 0 : s.metadata
|
|
8972
|
+
};
|
|
8973
|
+
})
|
|
8974
|
+
});
|
|
8792
8975
|
return { sources: resolvedSources, graphData };
|
|
8793
8976
|
}
|
|
8794
8977
|
/** Rewrite the user query for better retrieval performance. */
|
|
@@ -9183,6 +9366,7 @@ ${csv.trim()}`);
|
|
|
9183
9366
|
// src/core/DatabaseStorage.ts
|
|
9184
9367
|
import * as fs from "fs";
|
|
9185
9368
|
import * as path from "path";
|
|
9369
|
+
import * as os from "os";
|
|
9186
9370
|
var DatabaseStorage = class {
|
|
9187
9371
|
constructor(config) {
|
|
9188
9372
|
// Dynamic PG Pool
|
|
@@ -9191,7 +9375,8 @@ var DatabaseStorage = class {
|
|
|
9191
9375
|
this.mongoClient = null;
|
|
9192
9376
|
this.mongoDbName = "";
|
|
9193
9377
|
// Filesystem fallback configuration
|
|
9194
|
-
this.
|
|
9378
|
+
this.isServerless = Boolean(process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.LAMBDA_TASK_ROOT);
|
|
9379
|
+
this.fallbackDir = this.isServerless ? path.join(os.tmpdir(), ".retrivora") : path.join(process.cwd(), ".retrivora");
|
|
9195
9380
|
this.historyFile = path.join(this.fallbackDir, "history.json");
|
|
9196
9381
|
this.feedbackFile = path.join(this.fallbackDir, "feedback.json");
|
|
9197
9382
|
var _a2, _b, _c, _d, _e;
|
|
@@ -9321,10 +9506,27 @@ var DatabaseStorage = class {
|
|
|
9321
9506
|
}
|
|
9322
9507
|
// Helper for FS fallback writes
|
|
9323
9508
|
writeLocalFile(filePath, data) {
|
|
9324
|
-
|
|
9325
|
-
|
|
9509
|
+
try {
|
|
9510
|
+
const dir = path.dirname(filePath);
|
|
9511
|
+
if (!fs.existsSync(dir)) {
|
|
9512
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
9513
|
+
}
|
|
9514
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
9515
|
+
} catch (err) {
|
|
9516
|
+
if (err.code === "EROFS") {
|
|
9517
|
+
try {
|
|
9518
|
+
const tmpDir = path.join(os.tmpdir(), ".retrivora");
|
|
9519
|
+
if (!fs.existsSync(tmpDir)) {
|
|
9520
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
9521
|
+
}
|
|
9522
|
+
const tmpFilePath = path.join(tmpDir, path.basename(filePath));
|
|
9523
|
+
fs.writeFileSync(tmpFilePath, JSON.stringify(data, null, 2), "utf-8");
|
|
9524
|
+
} catch (e) {
|
|
9525
|
+
}
|
|
9526
|
+
} else {
|
|
9527
|
+
console.warn(`[DatabaseStorage] Failed to write local fallback file: ${err.message}`);
|
|
9528
|
+
}
|
|
9326
9529
|
}
|
|
9327
|
-
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
9328
9530
|
}
|
|
9329
9531
|
// ─── Message History Operations ──────────────────────────────────────────
|
|
9330
9532
|
async saveMessage(sessionId, message) {
|