@retrivora-ai/rag-engine 2.0.1 → 2.0.3
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/FREE_TIER_ARCHITECTURE.md +232 -0
- package/README.md +9 -0
- package/dist/{ILLMProvider-DMxLyTdq.d.mts → ILLMProvider-0rRBYbVW.d.mts} +133 -1
- package/dist/{ILLMProvider-DMxLyTdq.d.ts → ILLMProvider-0rRBYbVW.d.ts} +133 -1
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +679 -15
- package/dist/handlers/index.mjs +679 -15
- package/dist/index-B1wGUlSL.d.mts +532 -0
- package/dist/{index-tzwc7UhY.d.ts → index-BIHHp_f6.d.ts} +1 -1
- package/dist/{index-DHsFLJXQ.d.mts → index-BvODr57d.d.mts} +1 -1
- package/dist/index-DcklhThn.d.ts +532 -0
- package/dist/index.css +51 -3
- package/dist/index.d.mts +6 -105
- package/dist/index.d.ts +6 -105
- package/dist/index.js +1918 -8
- package/dist/index.mjs +1913 -8
- package/dist/server.d.mts +51 -7
- package/dist/server.d.ts +51 -7
- package/dist/server.js +721 -15
- package/dist/server.mjs +704 -15
- package/package.json +9 -7
- package/src/app/page.tsx +54 -18
- package/src/components/ChatWindow.tsx +2 -2
- package/src/components/DocumentUpload.tsx +5 -4
- package/src/components/MessageBubble.tsx +2 -4
- package/src/core/Pipeline.ts +23 -5
- package/src/handlers/index.ts +48 -1
- package/src/index.ts +22 -4
- package/src/rendering/IntentClassifier.ts +167 -0
- package/src/rendering/RendererRegistry.ts +164 -0
- package/src/rendering/RuleEngine.ts +67 -0
- package/src/rendering/VisualizationDecisionEngine.ts +164 -0
- package/src/rendering/__tests__/VisualizationDecisionEngine.test.ts +237 -0
- package/src/rendering/index.ts +12 -0
- package/src/rendering/rules/Rule1SpecificInfoRule.ts +28 -0
- package/src/rendering/rules/Rule2ComparisonRule.ts +24 -0
- package/src/rendering/rules/Rule3ProductDiscoveryRule.ts +28 -0
- package/src/rendering/rules/Rule4AnalyticalRule.ts +38 -0
- package/src/rendering/rules/Rule5MixedResponseRule.ts +29 -0
- package/src/rendering/rules/Rule6SmallResultSetRule.ts +24 -0
- package/src/rendering/rules/Rule7LargeTableRule.ts +35 -0
- package/src/rendering/types.ts +96 -0
- package/src/server.ts +3 -2
- package/src/types/index.ts +48 -0
- package/src/utils/DocumentParser.ts +64 -28
- package/src/utils/UITransformer.ts +15 -4
- package/dist/index-CfkqZd2Y.d.ts +0 -197
- package/dist/index-xygonxpW.d.mts +0 -197
package/dist/server.mjs
CHANGED
|
@@ -5904,6 +5904,606 @@ var LLMRouter = class {
|
|
|
5904
5904
|
|
|
5905
5905
|
// src/utils/UITransformer.ts
|
|
5906
5906
|
init_synonyms();
|
|
5907
|
+
|
|
5908
|
+
// src/rendering/IntentClassifier.ts
|
|
5909
|
+
var IntentClassifier = class {
|
|
5910
|
+
/**
|
|
5911
|
+
* Fast, zero-latency classifier that maps a user query and optional data context
|
|
5912
|
+
* into a high-level IntentCategory.
|
|
5913
|
+
*/
|
|
5914
|
+
static classify(context) {
|
|
5915
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
5916
|
+
const signals = this.extractDataSignals(context);
|
|
5917
|
+
if (this.isInformationLookup(q)) {
|
|
5918
|
+
return { intent: "information_lookup", signals };
|
|
5919
|
+
}
|
|
5920
|
+
if (this.isComparisonQuery(q)) {
|
|
5921
|
+
return { intent: "comparison", signals };
|
|
5922
|
+
}
|
|
5923
|
+
if (this.isProductQuery(q) || signals.isProductLike && this.isRecommendationQuery(q)) {
|
|
5924
|
+
return { intent: "product_search", signals };
|
|
5925
|
+
}
|
|
5926
|
+
if (this.isTrendQuery(q) || signals.hasDateFields && signals.hasNumericFields && /\b(growth|trend|over time|monthly|yearly|weekly)\b/.test(q)) {
|
|
5927
|
+
return { intent: "trend_analysis", signals };
|
|
5928
|
+
}
|
|
5929
|
+
if (this.isRankingQuery(q)) {
|
|
5930
|
+
return { intent: "ranking", signals };
|
|
5931
|
+
}
|
|
5932
|
+
if (this.isAnalyticsQuery(q) || signals.hasNumericFields && signals.hasCategoricalFields && /\b(sales|revenue|performance|count|total|average|breakdown|share|percentage|correlation)\b/.test(q)) {
|
|
5933
|
+
return { intent: "analytics", signals };
|
|
5934
|
+
}
|
|
5935
|
+
if (this.isRecommendationQuery(q)) {
|
|
5936
|
+
return { intent: "recommendation", signals };
|
|
5937
|
+
}
|
|
5938
|
+
return { intent: "information_lookup", signals };
|
|
5939
|
+
}
|
|
5940
|
+
/**
|
|
5941
|
+
* Extract key data structure signals from retrieved documents.
|
|
5942
|
+
*/
|
|
5943
|
+
static extractDataSignals(context) {
|
|
5944
|
+
const docs = context.retrievedDocuments || [];
|
|
5945
|
+
const rowCount = docs.length;
|
|
5946
|
+
let hasNumericFields = false;
|
|
5947
|
+
let hasDateFields = false;
|
|
5948
|
+
let hasCategoricalFields = false;
|
|
5949
|
+
let isProductLike = false;
|
|
5950
|
+
let numericFieldCount = 0;
|
|
5951
|
+
let categoricalFieldCount = 0;
|
|
5952
|
+
if (docs.length > 0) {
|
|
5953
|
+
const keys = /* @__PURE__ */ new Set();
|
|
5954
|
+
let numericKeys = /* @__PURE__ */ new Set();
|
|
5955
|
+
let dateKeys = /* @__PURE__ */ new Set();
|
|
5956
|
+
let catKeys = /* @__PURE__ */ new Set();
|
|
5957
|
+
let productKeyMatches = 0;
|
|
5958
|
+
docs.forEach((doc) => {
|
|
5959
|
+
const meta = doc.metadata || {};
|
|
5960
|
+
Object.entries(meta).forEach(([key, val]) => {
|
|
5961
|
+
keys.add(key);
|
|
5962
|
+
const kLower = key.toLowerCase();
|
|
5963
|
+
if (["price", "image", "brand", "in_stock", "rating", "sku", "product"].some((p) => kLower.includes(p))) {
|
|
5964
|
+
productKeyMatches++;
|
|
5965
|
+
}
|
|
5966
|
+
if (typeof val === "number") {
|
|
5967
|
+
numericKeys.add(key);
|
|
5968
|
+
} else if (typeof val === "string") {
|
|
5969
|
+
if (/^\d{4}-\d{2}-\d{2}/.test(val) || !isNaN(Date.parse(val)) && val.length > 5) {
|
|
5970
|
+
dateKeys.add(key);
|
|
5971
|
+
} else if (val.length < 50 && !val.includes("\n")) {
|
|
5972
|
+
catKeys.add(key);
|
|
5973
|
+
}
|
|
5974
|
+
}
|
|
5975
|
+
});
|
|
5976
|
+
});
|
|
5977
|
+
hasNumericFields = numericKeys.size > 0;
|
|
5978
|
+
hasDateFields = dateKeys.size > 0;
|
|
5979
|
+
hasCategoricalFields = catKeys.size > 0;
|
|
5980
|
+
numericFieldCount = numericKeys.size;
|
|
5981
|
+
categoricalFieldCount = catKeys.size;
|
|
5982
|
+
if (productKeyMatches >= 2 || docs.some((d) => d.content.toLowerCase().includes("price") || d.content.toLowerCase().includes("brand"))) {
|
|
5983
|
+
isProductLike = true;
|
|
5984
|
+
}
|
|
5985
|
+
}
|
|
5986
|
+
return {
|
|
5987
|
+
rowCount,
|
|
5988
|
+
hasNumericFields,
|
|
5989
|
+
hasDateFields,
|
|
5990
|
+
hasCategoricalFields,
|
|
5991
|
+
isProductLike,
|
|
5992
|
+
numericFieldCount,
|
|
5993
|
+
categoricalFieldCount
|
|
5994
|
+
};
|
|
5995
|
+
}
|
|
5996
|
+
// ─── Intent Heuristic Checkers ─────────────────────────────────────────────
|
|
5997
|
+
static isInformationLookup(query) {
|
|
5998
|
+
if (/^(what|who|explain|define|where|when|why|how|how does|can|could|does|do|is|are|has|have|will|should|would|meaning of)\s+/i.test(query)) {
|
|
5999
|
+
if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top selling|best)\b/i.test(query)) {
|
|
6000
|
+
return true;
|
|
6001
|
+
}
|
|
6002
|
+
}
|
|
6003
|
+
if (/^(what is the )?(price|cost|fee|rate) of\b/i.test(query)) {
|
|
6004
|
+
return true;
|
|
6005
|
+
}
|
|
6006
|
+
return false;
|
|
6007
|
+
}
|
|
6008
|
+
static isComparisonQuery(query) {
|
|
6009
|
+
return /\b(compare|comparison|vs\.?|versus|difference between|differ|contrast|against)\b/i.test(query);
|
|
6010
|
+
}
|
|
6011
|
+
static isProductQuery(query) {
|
|
6012
|
+
return /\b(best|recommend|top|buy|shopping|under \$|cheap|shoes|laptops|chairs|headphones|phone|sneakers|apparel|products)\b/i.test(query) && !/\b(compare|chart|sales|revenue|trend)\b/i.test(query);
|
|
6013
|
+
}
|
|
6014
|
+
static isRecommendationQuery(query) {
|
|
6015
|
+
return /\b(recommend|suggest|what should i (buy|get|choose)|best options|top picks)\b/i.test(query);
|
|
6016
|
+
}
|
|
6017
|
+
static isTrendQuery(query) {
|
|
6018
|
+
return /\b(trend|trends|growth|over time|historical|timeline|monthly|yearly|weekly|daily|revenue growth|sales growth)\b/i.test(query);
|
|
6019
|
+
}
|
|
6020
|
+
static isRankingQuery(query) {
|
|
6021
|
+
return /\b(highest|lowest|top\s*\d+|bottom\s*\d+|rank|ranking|top performing|best performing)\b/i.test(query);
|
|
6022
|
+
}
|
|
6023
|
+
static isAnalyticsQuery(query) {
|
|
6024
|
+
return /\b(sales|revenue|analytics|insights|distribution|percentage|share|breakdown|breakup|scatter|correlation|metrics|performance|by region|by category)\b/i.test(query);
|
|
6025
|
+
}
|
|
6026
|
+
};
|
|
6027
|
+
|
|
6028
|
+
// src/rendering/rules/Rule1SpecificInfoRule.ts
|
|
6029
|
+
var Rule1SpecificInfoRule = class {
|
|
6030
|
+
constructor() {
|
|
6031
|
+
this.name = "Rule1SpecificInfo";
|
|
6032
|
+
this.priority = 100;
|
|
6033
|
+
}
|
|
6034
|
+
evaluate(context, intent) {
|
|
6035
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
6036
|
+
const isFactQuery = intent === "information_lookup" || /^(what|who|explain|define|where|when|why|meaning of)\s+/i.test(q) || /^(what is the price of|price of|cost of)\b/i.test(q);
|
|
6037
|
+
const isNonVisual = !/\b(compare|versus|vs\.?|trend|monthly|revenue|best|top 10|chart|table|list|grid)\b/i.test(q);
|
|
6038
|
+
if (isFactQuery && isNonVisual) {
|
|
6039
|
+
return {
|
|
6040
|
+
renderType: "text",
|
|
6041
|
+
confidence: 0.95,
|
|
6042
|
+
reason: "Rule 1: Specific information request (fact, explanation, or definition) requires plain text rendering."
|
|
6043
|
+
};
|
|
6044
|
+
}
|
|
6045
|
+
return null;
|
|
6046
|
+
}
|
|
6047
|
+
};
|
|
6048
|
+
|
|
6049
|
+
// src/rendering/rules/Rule2ComparisonRule.ts
|
|
6050
|
+
var Rule2ComparisonRule = class {
|
|
6051
|
+
constructor() {
|
|
6052
|
+
this.name = "Rule2Comparison";
|
|
6053
|
+
this.priority = 90;
|
|
6054
|
+
}
|
|
6055
|
+
evaluate(context, intent) {
|
|
6056
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
6057
|
+
const isComparison = intent === "comparison" || /\b(compare|comparison|vs\.?|versus|difference between|differ|contrast|against)\b/i.test(q);
|
|
6058
|
+
if (isComparison) {
|
|
6059
|
+
return {
|
|
6060
|
+
renderType: "table",
|
|
6061
|
+
confidence: 0.9,
|
|
6062
|
+
reason: "Rule 2: Comparison query contrasting multiple entities requires structured table rendering."
|
|
6063
|
+
};
|
|
6064
|
+
}
|
|
6065
|
+
return null;
|
|
6066
|
+
}
|
|
6067
|
+
};
|
|
6068
|
+
|
|
6069
|
+
// src/rendering/rules/Rule3ProductDiscoveryRule.ts
|
|
6070
|
+
var Rule3ProductDiscoveryRule = class {
|
|
6071
|
+
constructor() {
|
|
6072
|
+
this.name = "Rule3ProductDiscovery";
|
|
6073
|
+
this.priority = 85;
|
|
6074
|
+
}
|
|
6075
|
+
evaluate(context, intent) {
|
|
6076
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
6077
|
+
const isDiscovery = intent === "product_search" || intent === "recommendation" || /\b(best|recommend|top|buy|shopping|under \$|cheap|shoes|laptops|chairs|headphones|sneakers|apparel|products|show available)\b/i.test(q);
|
|
6078
|
+
const isNotChart = !/\b(chart|sales|revenue|monthly|growth|trend|over time)\b/i.test(q);
|
|
6079
|
+
const isAnalyticalRanking = /\b(top performing|top selling|best performing|performing|sales|revenue|ranking)\b/i.test(q);
|
|
6080
|
+
if (isDiscovery && isNotChart && !isAnalyticalRanking) {
|
|
6081
|
+
return {
|
|
6082
|
+
renderType: "carousel",
|
|
6083
|
+
confidence: 0.9,
|
|
6084
|
+
reason: "Rule 3: Product discovery/recommendation query requires product carousel rendering."
|
|
6085
|
+
};
|
|
6086
|
+
}
|
|
6087
|
+
return null;
|
|
6088
|
+
}
|
|
6089
|
+
};
|
|
6090
|
+
|
|
6091
|
+
// src/rendering/rules/Rule4AnalyticalRule.ts
|
|
6092
|
+
var Rule4AnalyticalRule = class {
|
|
6093
|
+
constructor() {
|
|
6094
|
+
this.name = "Rule4Analytical";
|
|
6095
|
+
this.priority = 80;
|
|
6096
|
+
}
|
|
6097
|
+
evaluate(context, intent) {
|
|
6098
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
6099
|
+
const isAnalytical = intent === "analytics" || intent === "trend_analysis" || intent === "ranking" || /\b(monthly sales|revenue growth|top performing|population trends|sales by|growth|distribution|share|percentage|breakdown|correlation|scatter|trend)\b/i.test(q);
|
|
6100
|
+
if (!isAnalytical) return null;
|
|
6101
|
+
let chartType = "bar";
|
|
6102
|
+
if (intent === "trend_analysis" || /\b(monthly|yearly|weekly|growth|over time|trend|timeline|historical)\b/i.test(q)) {
|
|
6103
|
+
chartType = "line";
|
|
6104
|
+
} else if (/\b(share|percentage|percent|breakup|breakdown|composition|pie|donut|proportion)\b/i.test(q)) {
|
|
6105
|
+
chartType = "pie";
|
|
6106
|
+
} else if (/\b(correlation|scatter|relationship|relation|impact of|depends on)\b/i.test(q)) {
|
|
6107
|
+
chartType = "scatter";
|
|
6108
|
+
} else {
|
|
6109
|
+
chartType = "bar";
|
|
6110
|
+
}
|
|
6111
|
+
return {
|
|
6112
|
+
renderType: "chart",
|
|
6113
|
+
chartType,
|
|
6114
|
+
confidence: 0.85,
|
|
6115
|
+
reason: `Rule 4: Analytical query with numerical insights selects ${chartType} chart.`
|
|
6116
|
+
};
|
|
6117
|
+
}
|
|
6118
|
+
};
|
|
6119
|
+
|
|
6120
|
+
// src/rendering/rules/Rule5MixedResponseRule.ts
|
|
6121
|
+
var Rule5MixedResponseRule = class {
|
|
6122
|
+
constructor() {
|
|
6123
|
+
this.name = "Rule5MixedResponse";
|
|
6124
|
+
this.priority = 95;
|
|
6125
|
+
}
|
|
6126
|
+
evaluate(context, _intent) {
|
|
6127
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
6128
|
+
const isTopSellingThisMonth = /\btop\s*(selling|performing)?\s*products?\s*(this|last)?\s*(month|year|week)\b/i.test(q);
|
|
6129
|
+
const hasMultipleSectionsRequest = /\b(summary|overview)\b/i.test(q) && /\b(carousel|products?|items?)\b/i.test(q) && /\b(chart|sales|trend|revenue)\b/i.test(q);
|
|
6130
|
+
if (isTopSellingThisMonth || hasMultipleSectionsRequest) {
|
|
6131
|
+
return {
|
|
6132
|
+
renderType: "mixed",
|
|
6133
|
+
confidence: 0.95,
|
|
6134
|
+
reason: "Rule 5: Mixed response query requires multi-section composition (text, carousel, chart).",
|
|
6135
|
+
sections: [
|
|
6136
|
+
{ type: "text", title: "Summary" },
|
|
6137
|
+
{ type: "carousel", title: "Top Products" },
|
|
6138
|
+
{ type: "chart", chartType: "bar", title: "Sales Performance" }
|
|
6139
|
+
]
|
|
6140
|
+
};
|
|
6141
|
+
}
|
|
6142
|
+
return null;
|
|
6143
|
+
}
|
|
6144
|
+
};
|
|
6145
|
+
|
|
6146
|
+
// src/rendering/rules/Rule6SmallResultSetRule.ts
|
|
6147
|
+
var Rule6SmallResultSetRule = class {
|
|
6148
|
+
constructor() {
|
|
6149
|
+
this.name = "Rule6SmallResultSet";
|
|
6150
|
+
this.priority = 110;
|
|
6151
|
+
}
|
|
6152
|
+
evaluate(context, _intent) {
|
|
6153
|
+
const docs = context.retrievedDocuments || [];
|
|
6154
|
+
if (docs.length > 0 && docs.length < 3) {
|
|
6155
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
6156
|
+
const isExplicitComparison = /\b(compare|versus|vs\.?)\b/i.test(q);
|
|
6157
|
+
return {
|
|
6158
|
+
renderType: isExplicitComparison ? "table" : "text",
|
|
6159
|
+
confidence: 0.98,
|
|
6160
|
+
reason: `Rule 6: Small result set (${docs.length} rows < 3). Charts avoided to prevent sparse or uninformative visualizations.`
|
|
6161
|
+
};
|
|
6162
|
+
}
|
|
6163
|
+
return null;
|
|
6164
|
+
}
|
|
6165
|
+
};
|
|
6166
|
+
|
|
6167
|
+
// src/rendering/rules/Rule7LargeTableRule.ts
|
|
6168
|
+
var Rule7LargeTableRule = class {
|
|
6169
|
+
constructor() {
|
|
6170
|
+
this.name = "Rule7LargeTable";
|
|
6171
|
+
this.priority = 75;
|
|
6172
|
+
}
|
|
6173
|
+
evaluate(context, intent) {
|
|
6174
|
+
const docs = context.retrievedDocuments || [];
|
|
6175
|
+
if (docs.length > 5) {
|
|
6176
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
6177
|
+
const hasNumericAgg = intent === "analytics" || intent === "ranking" || /\b(sales|revenue|growth|count|total|average)\b/i.test(q);
|
|
6178
|
+
if (hasNumericAgg) {
|
|
6179
|
+
return {
|
|
6180
|
+
renderType: "mixed",
|
|
6181
|
+
confidence: 0.88,
|
|
6182
|
+
reason: `Rule 7: Large result set (${docs.length} rows > 5) with numeric aggregation rendered as Table + Chart.`,
|
|
6183
|
+
sections: [
|
|
6184
|
+
{ type: "table", title: "Detailed Data Table" },
|
|
6185
|
+
{ type: "chart", chartType: "bar", title: "Numerical Breakdown" }
|
|
6186
|
+
]
|
|
6187
|
+
};
|
|
6188
|
+
}
|
|
6189
|
+
return {
|
|
6190
|
+
renderType: "table",
|
|
6191
|
+
confidence: 0.85,
|
|
6192
|
+
reason: `Rule 7: Large result set (${docs.length} rows > 5) rendered as structured table for readability.`
|
|
6193
|
+
};
|
|
6194
|
+
}
|
|
6195
|
+
return null;
|
|
6196
|
+
}
|
|
6197
|
+
};
|
|
6198
|
+
|
|
6199
|
+
// src/rendering/RuleEngine.ts
|
|
6200
|
+
var RuleEngine = class {
|
|
6201
|
+
constructor() {
|
|
6202
|
+
this.rules = [];
|
|
6203
|
+
this.registerDefaultRules();
|
|
6204
|
+
}
|
|
6205
|
+
/**
|
|
6206
|
+
* Register standard rules in priority order.
|
|
6207
|
+
*/
|
|
6208
|
+
registerDefaultRules() {
|
|
6209
|
+
this.registerRule(new Rule6SmallResultSetRule());
|
|
6210
|
+
this.registerRule(new Rule1SpecificInfoRule());
|
|
6211
|
+
this.registerRule(new Rule5MixedResponseRule());
|
|
6212
|
+
this.registerRule(new Rule2ComparisonRule());
|
|
6213
|
+
this.registerRule(new Rule3ProductDiscoveryRule());
|
|
6214
|
+
this.registerRule(new Rule4AnalyticalRule());
|
|
6215
|
+
this.registerRule(new Rule7LargeTableRule());
|
|
6216
|
+
}
|
|
6217
|
+
/**
|
|
6218
|
+
* Add or replace a rule in the engine.
|
|
6219
|
+
* Automatically keeps rules sorted by descending priority.
|
|
6220
|
+
*/
|
|
6221
|
+
registerRule(rule) {
|
|
6222
|
+
this.rules = this.rules.filter((r) => r.name !== rule.name);
|
|
6223
|
+
this.rules.push(rule);
|
|
6224
|
+
this.rules.sort((a, b) => b.priority - a.priority);
|
|
6225
|
+
}
|
|
6226
|
+
/**
|
|
6227
|
+
* Evaluate context against rules in descending priority order.
|
|
6228
|
+
* Returns the first non-null RenderDecision.
|
|
6229
|
+
*/
|
|
6230
|
+
evaluate(context, intent) {
|
|
6231
|
+
for (const rule of this.rules) {
|
|
6232
|
+
const decision = rule.evaluate(context, intent);
|
|
6233
|
+
if (decision) {
|
|
6234
|
+
return decision;
|
|
6235
|
+
}
|
|
6236
|
+
}
|
|
6237
|
+
return {
|
|
6238
|
+
renderType: "text",
|
|
6239
|
+
confidence: 0.7,
|
|
6240
|
+
reason: "Default fallback: No specific visualization rule matched context."
|
|
6241
|
+
};
|
|
6242
|
+
}
|
|
6243
|
+
/**
|
|
6244
|
+
* List all registered rules.
|
|
6245
|
+
*/
|
|
6246
|
+
getRegisteredRules() {
|
|
6247
|
+
return this.rules.map((r) => ({ name: r.name, priority: r.priority }));
|
|
6248
|
+
}
|
|
6249
|
+
};
|
|
6250
|
+
|
|
6251
|
+
// src/rendering/RendererRegistry.ts
|
|
6252
|
+
var TextRendererStrategy = class {
|
|
6253
|
+
constructor() {
|
|
6254
|
+
this.type = "text";
|
|
6255
|
+
}
|
|
6256
|
+
render(data, options) {
|
|
6257
|
+
const title = (options == null ? void 0 : options.title) || "Information";
|
|
6258
|
+
if (typeof data === "string") {
|
|
6259
|
+
return { type: "text", title, data: { content: data } };
|
|
6260
|
+
}
|
|
6261
|
+
const docs = Array.isArray(data) ? data : [];
|
|
6262
|
+
const text = docs.map((d) => d.content).join("\n\n");
|
|
6263
|
+
return { type: "text", title, data: { content: text || "No detailed content available." } };
|
|
6264
|
+
}
|
|
6265
|
+
};
|
|
6266
|
+
var TableRendererStrategy = class {
|
|
6267
|
+
constructor() {
|
|
6268
|
+
this.type = "table";
|
|
6269
|
+
}
|
|
6270
|
+
render(data, options) {
|
|
6271
|
+
const docs = Array.isArray(data) ? data : [];
|
|
6272
|
+
const query = (options == null ? void 0 : options.query) || "";
|
|
6273
|
+
return UITransformer.transform(query, docs, void 0, void 0, {
|
|
6274
|
+
visualizationHint: "table",
|
|
6275
|
+
recommendedChart: "table",
|
|
6276
|
+
filterInStockOnly: false,
|
|
6277
|
+
wantsExplicitTable: true,
|
|
6278
|
+
isTemporal: false,
|
|
6279
|
+
isComparison: true,
|
|
6280
|
+
language: "en"
|
|
6281
|
+
});
|
|
6282
|
+
}
|
|
6283
|
+
};
|
|
6284
|
+
var CarouselRendererStrategy = class {
|
|
6285
|
+
constructor() {
|
|
6286
|
+
this.type = "carousel";
|
|
6287
|
+
}
|
|
6288
|
+
render(data, options) {
|
|
6289
|
+
const docs = Array.isArray(data) ? data : [];
|
|
6290
|
+
const query = (options == null ? void 0 : options.query) || "";
|
|
6291
|
+
return UITransformer.transform(query, docs, void 0, void 0, {
|
|
6292
|
+
visualizationHint: "product_browse",
|
|
6293
|
+
recommendedChart: "text",
|
|
6294
|
+
filterInStockOnly: false,
|
|
6295
|
+
wantsExplicitTable: false,
|
|
6296
|
+
isTemporal: false,
|
|
6297
|
+
isComparison: false,
|
|
6298
|
+
language: "en"
|
|
6299
|
+
});
|
|
6300
|
+
}
|
|
6301
|
+
};
|
|
6302
|
+
var ChartRendererStrategy = class {
|
|
6303
|
+
constructor() {
|
|
6304
|
+
this.type = "chart";
|
|
6305
|
+
}
|
|
6306
|
+
render(data, options) {
|
|
6307
|
+
const docs = Array.isArray(data) ? data : [];
|
|
6308
|
+
const query = (options == null ? void 0 : options.query) || "";
|
|
6309
|
+
const chartType = (options == null ? void 0 : options.chartType) || "bar";
|
|
6310
|
+
let hint = "comparison";
|
|
6311
|
+
let rec = "bar_chart";
|
|
6312
|
+
if (chartType === "line") {
|
|
6313
|
+
hint = "trend";
|
|
6314
|
+
rec = "line_chart";
|
|
6315
|
+
} else if (chartType === "pie") {
|
|
6316
|
+
hint = "composition";
|
|
6317
|
+
rec = "pie_chart";
|
|
6318
|
+
} else if (chartType === "scatter") {
|
|
6319
|
+
hint = "correlation";
|
|
6320
|
+
rec = "scatter_plot";
|
|
6321
|
+
}
|
|
6322
|
+
return UITransformer.transform(query, docs, void 0, void 0, {
|
|
6323
|
+
visualizationHint: hint,
|
|
6324
|
+
recommendedChart: rec,
|
|
6325
|
+
filterInStockOnly: false,
|
|
6326
|
+
wantsExplicitTable: false,
|
|
6327
|
+
isTemporal: chartType === "line",
|
|
6328
|
+
isComparison: chartType === "bar",
|
|
6329
|
+
language: "en"
|
|
6330
|
+
});
|
|
6331
|
+
}
|
|
6332
|
+
};
|
|
6333
|
+
var MixedRendererStrategy = class {
|
|
6334
|
+
constructor() {
|
|
6335
|
+
this.type = "mixed";
|
|
6336
|
+
}
|
|
6337
|
+
render(data, options) {
|
|
6338
|
+
const docs = Array.isArray(data) ? data : [];
|
|
6339
|
+
const sections = (options == null ? void 0 : options.sections) || [];
|
|
6340
|
+
const sectionPayloads = sections.map((s) => {
|
|
6341
|
+
const strategy = RendererRegistry.getStrategy(s.type);
|
|
6342
|
+
return {
|
|
6343
|
+
type: s.type,
|
|
6344
|
+
title: s.title,
|
|
6345
|
+
payload: strategy.render(data, __spreadProps(__spreadValues({}, options), { chartType: s.chartType, title: s.title }))
|
|
6346
|
+
};
|
|
6347
|
+
});
|
|
6348
|
+
return {
|
|
6349
|
+
type: "table",
|
|
6350
|
+
// Fallback container type for compatibility
|
|
6351
|
+
title: (options == null ? void 0 : options.title) || "Composite Response",
|
|
6352
|
+
description: `Mixed payload containing ${sectionPayloads.length} visual sections`,
|
|
6353
|
+
data: { sections: sectionPayloads }
|
|
6354
|
+
};
|
|
6355
|
+
}
|
|
6356
|
+
};
|
|
6357
|
+
var _RendererRegistry = class _RendererRegistry {
|
|
6358
|
+
/**
|
|
6359
|
+
* Register a new renderer strategy.
|
|
6360
|
+
* Enables seamless addition of custom visualizers (Timeline, Maps, Flow Diagrams, etc.).
|
|
6361
|
+
*/
|
|
6362
|
+
static registerStrategy(strategy) {
|
|
6363
|
+
this.strategies.set(String(strategy.type).toLowerCase(), strategy);
|
|
6364
|
+
}
|
|
6365
|
+
/**
|
|
6366
|
+
* Retrieve a registered renderer strategy by type string.
|
|
6367
|
+
*/
|
|
6368
|
+
static getStrategy(type) {
|
|
6369
|
+
const key = type.toLowerCase();
|
|
6370
|
+
const strategy = this.strategies.get(key);
|
|
6371
|
+
if (!strategy) {
|
|
6372
|
+
return this.strategies.get("text");
|
|
6373
|
+
}
|
|
6374
|
+
return strategy;
|
|
6375
|
+
}
|
|
6376
|
+
/**
|
|
6377
|
+
* Check if a renderer strategy is registered.
|
|
6378
|
+
*/
|
|
6379
|
+
static hasStrategy(type) {
|
|
6380
|
+
return this.strategies.has(type.toLowerCase());
|
|
6381
|
+
}
|
|
6382
|
+
/**
|
|
6383
|
+
* List all registered strategy types.
|
|
6384
|
+
*/
|
|
6385
|
+
static getRegisteredTypes() {
|
|
6386
|
+
return Array.from(this.strategies.keys());
|
|
6387
|
+
}
|
|
6388
|
+
};
|
|
6389
|
+
_RendererRegistry.strategies = /* @__PURE__ */ new Map();
|
|
6390
|
+
_RendererRegistry.registerStrategy(new TextRendererStrategy());
|
|
6391
|
+
_RendererRegistry.registerStrategy(new TableRendererStrategy());
|
|
6392
|
+
_RendererRegistry.registerStrategy(new CarouselRendererStrategy());
|
|
6393
|
+
_RendererRegistry.registerStrategy(new ChartRendererStrategy());
|
|
6394
|
+
_RendererRegistry.registerStrategy(new MixedRendererStrategy());
|
|
6395
|
+
var RendererRegistry = _RendererRegistry;
|
|
6396
|
+
|
|
6397
|
+
// src/rendering/VisualizationDecisionEngine.ts
|
|
6398
|
+
var LRUDecisionCache = class {
|
|
6399
|
+
constructor(maxSize = 200) {
|
|
6400
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
6401
|
+
this.maxSize = maxSize;
|
|
6402
|
+
}
|
|
6403
|
+
generateKey(context) {
|
|
6404
|
+
var _a2, _b;
|
|
6405
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
6406
|
+
const docCount = (_b = (_a2 = context.retrievedDocuments) == null ? void 0 : _a2.length) != null ? _b : 0;
|
|
6407
|
+
return `${q}::docs:${docCount}`;
|
|
6408
|
+
}
|
|
6409
|
+
get(context) {
|
|
6410
|
+
const key = this.generateKey(context);
|
|
6411
|
+
const item = this.cache.get(key);
|
|
6412
|
+
if (item) {
|
|
6413
|
+
this.cache.delete(key);
|
|
6414
|
+
this.cache.set(key, item);
|
|
6415
|
+
return item.decision;
|
|
6416
|
+
}
|
|
6417
|
+
return void 0;
|
|
6418
|
+
}
|
|
6419
|
+
set(context, decision) {
|
|
6420
|
+
const key = this.generateKey(context);
|
|
6421
|
+
if (this.cache.has(key)) {
|
|
6422
|
+
this.cache.delete(key);
|
|
6423
|
+
} else if (this.cache.size >= this.maxSize) {
|
|
6424
|
+
const oldest = this.cache.keys().next().value;
|
|
6425
|
+
if (oldest !== void 0) this.cache.delete(oldest);
|
|
6426
|
+
}
|
|
6427
|
+
this.cache.set(key, { decision, timestamp: Date.now() });
|
|
6428
|
+
}
|
|
6429
|
+
clear() {
|
|
6430
|
+
this.cache.clear();
|
|
6431
|
+
}
|
|
6432
|
+
get size() {
|
|
6433
|
+
return this.cache.size;
|
|
6434
|
+
}
|
|
6435
|
+
};
|
|
6436
|
+
var VisualizationDecisionEngine = class {
|
|
6437
|
+
/**
|
|
6438
|
+
* Evaluate user query, documents, and context to produce a RenderDecision.
|
|
6439
|
+
* Execution time is < 1ms for cached or rule-matched queries.
|
|
6440
|
+
*/
|
|
6441
|
+
static decideVisualization(userQuery, retrievedDocuments, llmResponse, metadata) {
|
|
6442
|
+
const context = {
|
|
6443
|
+
userQuery,
|
|
6444
|
+
retrievedDocuments,
|
|
6445
|
+
llmResponse,
|
|
6446
|
+
metadata
|
|
6447
|
+
};
|
|
6448
|
+
const cached = this.cache.get(context);
|
|
6449
|
+
if (cached) {
|
|
6450
|
+
return cached;
|
|
6451
|
+
}
|
|
6452
|
+
const { intent } = IntentClassifier.classify(context);
|
|
6453
|
+
const decision = this.ruleEngine.evaluate(context, intent);
|
|
6454
|
+
this.cache.set(context, decision);
|
|
6455
|
+
return decision;
|
|
6456
|
+
}
|
|
6457
|
+
/**
|
|
6458
|
+
* Convenience helper to decide AND render the data into a UITransformationResponse.
|
|
6459
|
+
*/
|
|
6460
|
+
static render(userQuery, retrievedDocuments, llmResponse, metadata) {
|
|
6461
|
+
const decision = this.decideVisualization(userQuery, retrievedDocuments, llmResponse, metadata);
|
|
6462
|
+
const strategy = RendererRegistry.getStrategy(decision.renderType);
|
|
6463
|
+
return strategy.render(retrievedDocuments || [], __spreadValues({
|
|
6464
|
+
query: userQuery,
|
|
6465
|
+
chartType: decision.chartType,
|
|
6466
|
+
sections: decision.sections
|
|
6467
|
+
}, metadata));
|
|
6468
|
+
}
|
|
6469
|
+
/**
|
|
6470
|
+
* Register a custom rule in the rule engine.
|
|
6471
|
+
*/
|
|
6472
|
+
static registerRule(rule) {
|
|
6473
|
+
this.ruleEngine.registerRule(rule);
|
|
6474
|
+
this.cache.clear();
|
|
6475
|
+
}
|
|
6476
|
+
/**
|
|
6477
|
+
* Register a custom renderer strategy.
|
|
6478
|
+
*/
|
|
6479
|
+
static registerRendererStrategy(strategy) {
|
|
6480
|
+
RendererRegistry.registerStrategy(strategy);
|
|
6481
|
+
}
|
|
6482
|
+
/**
|
|
6483
|
+
* Clear the decision cache.
|
|
6484
|
+
*/
|
|
6485
|
+
static clearCache() {
|
|
6486
|
+
this.cache.clear();
|
|
6487
|
+
}
|
|
6488
|
+
/**
|
|
6489
|
+
* Get size of current decision cache.
|
|
6490
|
+
*/
|
|
6491
|
+
static getCacheSize() {
|
|
6492
|
+
return this.cache.size;
|
|
6493
|
+
}
|
|
6494
|
+
};
|
|
6495
|
+
VisualizationDecisionEngine.ruleEngine = new RuleEngine();
|
|
6496
|
+
VisualizationDecisionEngine.cache = new LRUDecisionCache(200);
|
|
6497
|
+
function decideVisualization(userQuery, retrievedDocuments, llmResponse, metadata) {
|
|
6498
|
+
return VisualizationDecisionEngine.decideVisualization(
|
|
6499
|
+
userQuery,
|
|
6500
|
+
retrievedDocuments,
|
|
6501
|
+
llmResponse,
|
|
6502
|
+
metadata
|
|
6503
|
+
);
|
|
6504
|
+
}
|
|
6505
|
+
|
|
6506
|
+
// src/utils/UITransformer.ts
|
|
5907
6507
|
var UITransformer = class _UITransformer {
|
|
5908
6508
|
// ─── Public Entry Points ─────────────────────────────────────────────────
|
|
5909
6509
|
/**
|
|
@@ -5950,6 +6550,9 @@ var UITransformer = class _UITransformer {
|
|
|
5950
6550
|
return this.hasMultipleFields(filteredData) ? this.transformToTable(filteredData, userQuery) : this.transformToText(filteredData);
|
|
5951
6551
|
}
|
|
5952
6552
|
if ((hasProducts || this.isProductQuery(userQuery)) && resolvedIntent.visualizationHint === "product_browse") {
|
|
6553
|
+
if (!this.isProductQuery(userQuery)) {
|
|
6554
|
+
return this.transformToText(filteredData);
|
|
6555
|
+
}
|
|
5953
6556
|
return this.transformToProductCarousel(filteredData, config, trainedSchema);
|
|
5954
6557
|
}
|
|
5955
6558
|
const automatic = this.chooseAutomaticVisualization(filteredData, profile, userQuery);
|
|
@@ -5964,6 +6567,11 @@ var UITransformer = class _UITransformer {
|
|
|
5964
6567
|
* Step 3 — Fall back to the heuristic `transform()` if either LLM call fails.
|
|
5965
6568
|
*/
|
|
5966
6569
|
static async analyzeAndDecide(query, sources, llm) {
|
|
6570
|
+
const decision = VisualizationDecisionEngine.decideVisualization(query, sources);
|
|
6571
|
+
if (decision.renderType === "text") {
|
|
6572
|
+
console.debug("[UITransformer] Decision Engine selected plain text rendering \u2014 skipping visual LLM calls.");
|
|
6573
|
+
return this.transformToText(sources);
|
|
6574
|
+
}
|
|
5967
6575
|
let intent;
|
|
5968
6576
|
try {
|
|
5969
6577
|
intent = await this.detectIntent(query, llm, sources);
|
|
@@ -6413,7 +7021,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
6413
7021
|
}
|
|
6414
7022
|
static transformToText(data) {
|
|
6415
7023
|
return this.createTextResponse(
|
|
6416
|
-
"
|
|
7024
|
+
"Retrieved Context",
|
|
6417
7025
|
data.map((item) => item.content).join("\n\n"),
|
|
6418
7026
|
`Found ${data.length} relevant results`
|
|
6419
7027
|
);
|
|
@@ -6580,16 +7188,13 @@ ${schemaProfileText}` : ""}`;
|
|
|
6580
7188
|
"product",
|
|
6581
7189
|
"price",
|
|
6582
7190
|
"stock",
|
|
6583
|
-
"item",
|
|
6584
7191
|
"sku",
|
|
6585
7192
|
"brand",
|
|
6586
|
-
"model",
|
|
6587
7193
|
"msrp",
|
|
6588
7194
|
"inventory",
|
|
6589
7195
|
"buy",
|
|
6590
7196
|
"shop",
|
|
6591
7197
|
"beauty",
|
|
6592
|
-
"care",
|
|
6593
7198
|
"cosmetic",
|
|
6594
7199
|
"facial",
|
|
6595
7200
|
"cream",
|
|
@@ -6602,7 +7207,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
6602
7207
|
const metadata = item.metadata || {};
|
|
6603
7208
|
const hasMetadataKey = Object.keys(metadata).some((k) => {
|
|
6604
7209
|
const val = metadata[k];
|
|
6605
|
-
return ["price", "product", "sku", "brand", "
|
|
7210
|
+
return ["price", "product", "sku", "brand", "cost"].includes(k.toLowerCase()) && val !== null && val !== void 0 && val !== "";
|
|
6606
7211
|
});
|
|
6607
7212
|
const hasPricePattern = /\$\s*\d+\.\d{2}/.test(content);
|
|
6608
7213
|
return hasKeywords || hasMetadataKey || hasPricePattern;
|
|
@@ -7291,6 +7896,13 @@ SchemaMapper.TARGET_PROPERTIES = {
|
|
|
7291
7896
|
};
|
|
7292
7897
|
|
|
7293
7898
|
// src/core/Pipeline.ts
|
|
7899
|
+
function formatNamespace(raw) {
|
|
7900
|
+
if (!raw || !raw.trim()) return "retrivora-default";
|
|
7901
|
+
const trimmed = raw.trim();
|
|
7902
|
+
if (trimmed.startsWith("retrivora-")) return trimmed;
|
|
7903
|
+
const sanitized = trimmed.toLowerCase().replace(/[^a-z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
7904
|
+
return `retrivora-${sanitized}`;
|
|
7905
|
+
}
|
|
7294
7906
|
var LRUEmbeddingCache = class {
|
|
7295
7907
|
constructor(maxSize = 500) {
|
|
7296
7908
|
this.cache = /* @__PURE__ */ new Map();
|
|
@@ -7482,7 +8094,7 @@ ${m.content}`).join("\n\n---\n\n");
|
|
|
7482
8094
|
*/
|
|
7483
8095
|
async ingest(documents, namespace) {
|
|
7484
8096
|
await this.initialize();
|
|
7485
|
-
const ns = namespace != null ? namespace : this.config.projectId;
|
|
8097
|
+
const ns = formatNamespace(namespace != null ? namespace : this.config.projectId);
|
|
7486
8098
|
const results = [];
|
|
7487
8099
|
for (const doc of documents) {
|
|
7488
8100
|
try {
|
|
@@ -7701,7 +8313,7 @@ ${m.content}`).join("\n\n---\n\n");
|
|
|
7701
8313
|
return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
|
|
7702
8314
|
var _a2, _b, _c, _d, _e, _f, _g2, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C;
|
|
7703
8315
|
yield new __await(this.initialize());
|
|
7704
|
-
const ns = namespace != null ? namespace : this.config.projectId;
|
|
8316
|
+
const ns = formatNamespace(namespace != null ? namespace : this.config.projectId);
|
|
7705
8317
|
const topK = (_b = (_a2 = this.config.rag) == null ? void 0 : _a2.topK) != null ? _b : 5;
|
|
7706
8318
|
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0.3;
|
|
7707
8319
|
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -7839,7 +8451,7 @@ ${context}`;
|
|
|
7839
8451
|
const isNativeThinking = isClaude37 && (((_p = this.config.llm.options) == null ? void 0 : _p.thinking) === true || ((_q = this.config.llm.options) == null ? void 0 : _q.thinking) === "enabled" || ((_r = this.config.llm.options) == null ? void 0 : _r.thinking) !== false);
|
|
7840
8452
|
const modelNameLower = this.config.llm.model.toLowerCase();
|
|
7841
8453
|
const isReasoningModel = modelNameLower.includes("-r1") || modelNameLower.includes("deepseek-r1") || modelNameLower.includes("reasoning") || modelNameLower.includes("thinking");
|
|
7842
|
-
const isSimulatedThinking = !isNativeThinking && (((_s = this.config.llm.options) == null ? void 0 : _s.thinking) === true || ((_t = this.config.llm.options) == null ? void 0 : _t.thinking) === "enabled" ||
|
|
8454
|
+
const isSimulatedThinking = !isNativeThinking && (((_s = this.config.llm.options) == null ? void 0 : _s.thinking) === true || ((_t = this.config.llm.options) == null ? void 0 : _t.thinking) === "enabled" || ((_u = this.config.llm.options) == null ? void 0 : _u.thinking) !== false);
|
|
7843
8455
|
let finalRestrictionSuffix = restrictionSuffix;
|
|
7844
8456
|
if (isSimulatedThinking) {
|
|
7845
8457
|
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.`)";
|
|
@@ -8165,7 +8777,7 @@ ${context}`;
|
|
|
8165
8777
|
}
|
|
8166
8778
|
async retrieve(query, options) {
|
|
8167
8779
|
var _a2, _b, _c, _d, _e, _f, _g2;
|
|
8168
|
-
const ns = (_a2 = options.namespace) != null ? _a2 : this.config.projectId;
|
|
8780
|
+
const ns = formatNamespace((_a2 = options.namespace) != null ? _a2 : this.config.projectId);
|
|
8169
8781
|
const topK = (_b = options.topK) != null ? _b : 5;
|
|
8170
8782
|
const cacheKey = `${ns}::${query}`;
|
|
8171
8783
|
let queryVector = this.embeddingCache.get(cacheKey);
|
|
@@ -8234,7 +8846,7 @@ Optimized Search Query:`;
|
|
|
8234
8846
|
async getSuggestions(query, namespace) {
|
|
8235
8847
|
if (!query || query.trim().length < 3) return [];
|
|
8236
8848
|
await this.initialize();
|
|
8237
|
-
const ns = namespace != null ? namespace : this.config.projectId;
|
|
8849
|
+
const ns = formatNamespace(namespace != null ? namespace : this.config.projectId);
|
|
8238
8850
|
try {
|
|
8239
8851
|
const { sources } = await this.retrieve(query, { namespace: ns, topK: 5 });
|
|
8240
8852
|
if (sources.length === 0) return [];
|
|
@@ -8804,7 +9416,7 @@ var DocumentParser = class {
|
|
|
8804
9416
|
*/
|
|
8805
9417
|
static async parse(file, fileName, mimeType) {
|
|
8806
9418
|
var _a2;
|
|
8807
|
-
const extension = (_a2 = fileName.split(".").pop()) == null ? void 0 : _a2.toLowerCase();
|
|
9419
|
+
const extension = ((_a2 = fileName.split(".").pop()) == null ? void 0 : _a2.toLowerCase()) || "";
|
|
8808
9420
|
if (extension === "txt" || extension === "md" || mimeType === "text/plain" || mimeType === "text/markdown") {
|
|
8809
9421
|
return this.readAsText(file);
|
|
8810
9422
|
}
|
|
@@ -8820,6 +9432,29 @@ var DocumentParser = class {
|
|
|
8820
9432
|
if (extension === "csv" || mimeType === "text/csv") {
|
|
8821
9433
|
return this.readAsText(file);
|
|
8822
9434
|
}
|
|
9435
|
+
if (extension === "xlsx" || extension === "xls" || mimeType.includes("spreadsheet") || mimeType.includes("excel")) {
|
|
9436
|
+
try {
|
|
9437
|
+
const xlsx = await import("xlsx");
|
|
9438
|
+
const buffer = Buffer.isBuffer(file) ? file : Buffer.from(await file.arrayBuffer());
|
|
9439
|
+
const workbook = xlsx.read(buffer, { type: "buffer" });
|
|
9440
|
+
const sheetsText = [];
|
|
9441
|
+
for (const sheetName of workbook.SheetNames) {
|
|
9442
|
+
const sheet = workbook.Sheets[sheetName];
|
|
9443
|
+
const csv = xlsx.utils.sheet_to_csv(sheet);
|
|
9444
|
+
if (csv && csv.trim()) {
|
|
9445
|
+
sheetsText.push(`--- Sheet: ${sheetName} ---
|
|
9446
|
+
${csv.trim()}`);
|
|
9447
|
+
}
|
|
9448
|
+
}
|
|
9449
|
+
return sheetsText.join("\n\n") || `[Empty Excel Workbook: ${fileName}]`;
|
|
9450
|
+
} catch (e) {
|
|
9451
|
+
try {
|
|
9452
|
+
return await this.readAsText(file);
|
|
9453
|
+
} catch (e2) {
|
|
9454
|
+
return `[Excel File Content: ${fileName}]`;
|
|
9455
|
+
}
|
|
9456
|
+
}
|
|
9457
|
+
}
|
|
8823
9458
|
if (extension === "pdf" || mimeType === "application/pdf") {
|
|
8824
9459
|
try {
|
|
8825
9460
|
const pdf = await import("pdf-parse");
|
|
@@ -8831,15 +9466,15 @@ var DocumentParser = class {
|
|
|
8831
9466
|
return `[PDF Parsing Error for ${fileName}]`;
|
|
8832
9467
|
}
|
|
8833
9468
|
}
|
|
8834
|
-
if (extension === "docx" || mimeType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document") {
|
|
9469
|
+
if (extension === "docx" || extension === "doc" || mimeType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document" || mimeType === "application/msword") {
|
|
8835
9470
|
try {
|
|
8836
9471
|
const mammoth = await import("mammoth");
|
|
8837
9472
|
const buffer = Buffer.isBuffer(file) ? file : Buffer.from(await file.arrayBuffer());
|
|
8838
9473
|
const result = await mammoth.extractRawText({ buffer });
|
|
8839
9474
|
return result.value;
|
|
8840
9475
|
} catch (err) {
|
|
8841
|
-
console.warn('[DocumentParser]
|
|
8842
|
-
return `[
|
|
9476
|
+
console.warn('[DocumentParser] Word parsing failed. Make sure "mammoth" is installed.', err);
|
|
9477
|
+
return `[Word Parsing Error for ${fileName}]`;
|
|
8843
9478
|
}
|
|
8844
9479
|
}
|
|
8845
9480
|
try {
|
|
@@ -9679,6 +10314,7 @@ function createUploadHandler(configOrPlugin, options) {
|
|
|
9679
10314
|
}
|
|
9680
10315
|
const documents = [];
|
|
9681
10316
|
for (const file of files) {
|
|
10317
|
+
const isExcel = file.name.toLowerCase().endsWith(".xlsx") || file.name.toLowerCase().endsWith(".xls") || file.type.includes("spreadsheet") || file.type.includes("excel");
|
|
9682
10318
|
if (file.name.toLowerCase().endsWith(".csv") || file.type === "text/csv") {
|
|
9683
10319
|
const text = await file.text();
|
|
9684
10320
|
const Papa = await import("papaparse");
|
|
@@ -9711,7 +10347,7 @@ function createUploadHandler(configOrPlugin, options) {
|
|
|
9711
10347
|
metadata: __spreadValues(__spreadProps(__spreadValues({
|
|
9712
10348
|
fileName: file.name,
|
|
9713
10349
|
fileSize: file.size,
|
|
9714
|
-
fileType: file.type,
|
|
10350
|
+
fileType: file.type || "text/csv",
|
|
9715
10351
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9716
10352
|
}, dimension ? { dimension } : {}), {
|
|
9717
10353
|
csvHeaders: csvCols
|
|
@@ -9719,6 +10355,42 @@ function createUploadHandler(configOrPlugin, options) {
|
|
|
9719
10355
|
});
|
|
9720
10356
|
}
|
|
9721
10357
|
}
|
|
10358
|
+
} else if (isExcel) {
|
|
10359
|
+
const xlsx = await import("xlsx");
|
|
10360
|
+
const buffer = Buffer.isBuffer(file) ? file : Buffer.from(await file.arrayBuffer());
|
|
10361
|
+
const workbook = xlsx.read(buffer, { type: "buffer" });
|
|
10362
|
+
for (const sheetName of workbook.SheetNames) {
|
|
10363
|
+
const sheet = workbook.Sheets[sheetName];
|
|
10364
|
+
const jsonRows = xlsx.utils.sheet_to_json(sheet, { defval: "" });
|
|
10365
|
+
if (jsonRows && jsonRows.length > 0) {
|
|
10366
|
+
let i = 0;
|
|
10367
|
+
const headers = Object.keys(jsonRows[0] || {});
|
|
10368
|
+
for (const rowData of jsonRows) {
|
|
10369
|
+
i++;
|
|
10370
|
+
const contentParts = [];
|
|
10371
|
+
for (const [key, val] of Object.entries(rowData)) {
|
|
10372
|
+
if (key && val !== void 0 && val !== null && String(val).trim()) {
|
|
10373
|
+
contentParts.push(`${key}: ${val}`);
|
|
10374
|
+
}
|
|
10375
|
+
}
|
|
10376
|
+
if (contentParts.length > 0) {
|
|
10377
|
+
documents.push({
|
|
10378
|
+
docId: `${file.name}-${sheetName}-row-${i}`,
|
|
10379
|
+
content: contentParts.join(", "),
|
|
10380
|
+
metadata: __spreadValues(__spreadProps(__spreadValues({
|
|
10381
|
+
fileName: file.name,
|
|
10382
|
+
sheetName,
|
|
10383
|
+
fileSize: file.size,
|
|
10384
|
+
fileType: file.type || "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
10385
|
+
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10386
|
+
}, dimension ? { dimension } : {}), {
|
|
10387
|
+
headers
|
|
10388
|
+
}), rowData)
|
|
10389
|
+
});
|
|
10390
|
+
}
|
|
10391
|
+
}
|
|
10392
|
+
}
|
|
10393
|
+
}
|
|
9722
10394
|
} else {
|
|
9723
10395
|
const content = await DocumentParser.parse(file, file.name, file.type);
|
|
9724
10396
|
documents.push({
|
|
@@ -9945,6 +10617,8 @@ export {
|
|
|
9945
10617
|
AuthenticationException,
|
|
9946
10618
|
BaseVectorProvider,
|
|
9947
10619
|
BatchProcessor,
|
|
10620
|
+
CarouselRendererStrategy,
|
|
10621
|
+
ChartRendererStrategy,
|
|
9948
10622
|
ChromaDBProvider,
|
|
9949
10623
|
ConfigBuilder,
|
|
9950
10624
|
ConfigResolver,
|
|
@@ -9956,10 +10630,12 @@ export {
|
|
|
9956
10630
|
EmbeddingStrategy,
|
|
9957
10631
|
EmbeddingStrategyResolver,
|
|
9958
10632
|
GroqProvider,
|
|
10633
|
+
IntentClassifier,
|
|
9959
10634
|
LLMFactory,
|
|
9960
10635
|
LLM_PROFILES,
|
|
9961
10636
|
LicenseVerifier,
|
|
9962
10637
|
MilvusProvider,
|
|
10638
|
+
MixedRendererStrategy,
|
|
9963
10639
|
MongoDBProvider,
|
|
9964
10640
|
MultiTablePostgresProvider,
|
|
9965
10641
|
OllamaProvider,
|
|
@@ -9975,13 +10651,25 @@ export {
|
|
|
9975
10651
|
QwenProvider,
|
|
9976
10652
|
RateLimitException,
|
|
9977
10653
|
RedisProvider,
|
|
10654
|
+
RendererRegistry,
|
|
9978
10655
|
RetrievalException,
|
|
9979
10656
|
Retrivora,
|
|
9980
10657
|
RetrivoraError,
|
|
10658
|
+
Rule1SpecificInfoRule,
|
|
10659
|
+
Rule2ComparisonRule,
|
|
10660
|
+
Rule3ProductDiscoveryRule,
|
|
10661
|
+
Rule4AnalyticalRule,
|
|
10662
|
+
Rule5MixedResponseRule,
|
|
10663
|
+
Rule6SmallResultSetRule,
|
|
10664
|
+
Rule7LargeTableRule,
|
|
10665
|
+
RuleEngine,
|
|
10666
|
+
TableRendererStrategy,
|
|
10667
|
+
TextRendererStrategy,
|
|
9981
10668
|
UniversalLLMAdapter,
|
|
9982
10669
|
UniversalVectorProvider,
|
|
9983
10670
|
VECTOR_PROFILES,
|
|
9984
10671
|
VectorPlugin,
|
|
10672
|
+
VisualizationDecisionEngine,
|
|
9985
10673
|
WeaviateProvider,
|
|
9986
10674
|
createChatHandler,
|
|
9987
10675
|
createFeedbackHandler,
|
|
@@ -9993,6 +10681,7 @@ export {
|
|
|
9993
10681
|
createSessionsHandler,
|
|
9994
10682
|
createStreamHandler,
|
|
9995
10683
|
createUploadHandler,
|
|
10684
|
+
decideVisualization,
|
|
9996
10685
|
getRagConfig,
|
|
9997
10686
|
sseErrorFrame,
|
|
9998
10687
|
sseFrame,
|