@retrivora-ai/rag-engine 2.0.2 → 2.0.4
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 +681 -17
- package/dist/handlers/index.mjs +681 -17
- 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 +723 -17
- package/dist/server.mjs +706 -17
- 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/DatabaseStorage.ts +5 -1
- package/src/core/LicenseVerifier.ts +1 -1
- 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/handlers/index.mjs
CHANGED
|
@@ -5654,6 +5654,598 @@ var LLMRouter = class {
|
|
|
5654
5654
|
|
|
5655
5655
|
// src/utils/UITransformer.ts
|
|
5656
5656
|
init_synonyms();
|
|
5657
|
+
|
|
5658
|
+
// src/rendering/IntentClassifier.ts
|
|
5659
|
+
var IntentClassifier = class {
|
|
5660
|
+
/**
|
|
5661
|
+
* Fast, zero-latency classifier that maps a user query and optional data context
|
|
5662
|
+
* into a high-level IntentCategory.
|
|
5663
|
+
*/
|
|
5664
|
+
static classify(context) {
|
|
5665
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
5666
|
+
const signals = this.extractDataSignals(context);
|
|
5667
|
+
if (this.isInformationLookup(q)) {
|
|
5668
|
+
return { intent: "information_lookup", signals };
|
|
5669
|
+
}
|
|
5670
|
+
if (this.isComparisonQuery(q)) {
|
|
5671
|
+
return { intent: "comparison", signals };
|
|
5672
|
+
}
|
|
5673
|
+
if (this.isProductQuery(q) || signals.isProductLike && this.isRecommendationQuery(q)) {
|
|
5674
|
+
return { intent: "product_search", signals };
|
|
5675
|
+
}
|
|
5676
|
+
if (this.isTrendQuery(q) || signals.hasDateFields && signals.hasNumericFields && /\b(growth|trend|over time|monthly|yearly|weekly)\b/.test(q)) {
|
|
5677
|
+
return { intent: "trend_analysis", signals };
|
|
5678
|
+
}
|
|
5679
|
+
if (this.isRankingQuery(q)) {
|
|
5680
|
+
return { intent: "ranking", signals };
|
|
5681
|
+
}
|
|
5682
|
+
if (this.isAnalyticsQuery(q) || signals.hasNumericFields && signals.hasCategoricalFields && /\b(sales|revenue|performance|count|total|average|breakdown|share|percentage|correlation)\b/.test(q)) {
|
|
5683
|
+
return { intent: "analytics", signals };
|
|
5684
|
+
}
|
|
5685
|
+
if (this.isRecommendationQuery(q)) {
|
|
5686
|
+
return { intent: "recommendation", signals };
|
|
5687
|
+
}
|
|
5688
|
+
return { intent: "information_lookup", signals };
|
|
5689
|
+
}
|
|
5690
|
+
/**
|
|
5691
|
+
* Extract key data structure signals from retrieved documents.
|
|
5692
|
+
*/
|
|
5693
|
+
static extractDataSignals(context) {
|
|
5694
|
+
const docs = context.retrievedDocuments || [];
|
|
5695
|
+
const rowCount = docs.length;
|
|
5696
|
+
let hasNumericFields = false;
|
|
5697
|
+
let hasDateFields = false;
|
|
5698
|
+
let hasCategoricalFields = false;
|
|
5699
|
+
let isProductLike = false;
|
|
5700
|
+
let numericFieldCount = 0;
|
|
5701
|
+
let categoricalFieldCount = 0;
|
|
5702
|
+
if (docs.length > 0) {
|
|
5703
|
+
const keys = /* @__PURE__ */ new Set();
|
|
5704
|
+
let numericKeys = /* @__PURE__ */ new Set();
|
|
5705
|
+
let dateKeys = /* @__PURE__ */ new Set();
|
|
5706
|
+
let catKeys = /* @__PURE__ */ new Set();
|
|
5707
|
+
let productKeyMatches = 0;
|
|
5708
|
+
docs.forEach((doc) => {
|
|
5709
|
+
const meta = doc.metadata || {};
|
|
5710
|
+
Object.entries(meta).forEach(([key, val]) => {
|
|
5711
|
+
keys.add(key);
|
|
5712
|
+
const kLower = key.toLowerCase();
|
|
5713
|
+
if (["price", "image", "brand", "in_stock", "rating", "sku", "product"].some((p) => kLower.includes(p))) {
|
|
5714
|
+
productKeyMatches++;
|
|
5715
|
+
}
|
|
5716
|
+
if (typeof val === "number") {
|
|
5717
|
+
numericKeys.add(key);
|
|
5718
|
+
} else if (typeof val === "string") {
|
|
5719
|
+
if (/^\d{4}-\d{2}-\d{2}/.test(val) || !isNaN(Date.parse(val)) && val.length > 5) {
|
|
5720
|
+
dateKeys.add(key);
|
|
5721
|
+
} else if (val.length < 50 && !val.includes("\n")) {
|
|
5722
|
+
catKeys.add(key);
|
|
5723
|
+
}
|
|
5724
|
+
}
|
|
5725
|
+
});
|
|
5726
|
+
});
|
|
5727
|
+
hasNumericFields = numericKeys.size > 0;
|
|
5728
|
+
hasDateFields = dateKeys.size > 0;
|
|
5729
|
+
hasCategoricalFields = catKeys.size > 0;
|
|
5730
|
+
numericFieldCount = numericKeys.size;
|
|
5731
|
+
categoricalFieldCount = catKeys.size;
|
|
5732
|
+
if (productKeyMatches >= 2 || docs.some((d) => d.content.toLowerCase().includes("price") || d.content.toLowerCase().includes("brand"))) {
|
|
5733
|
+
isProductLike = true;
|
|
5734
|
+
}
|
|
5735
|
+
}
|
|
5736
|
+
return {
|
|
5737
|
+
rowCount,
|
|
5738
|
+
hasNumericFields,
|
|
5739
|
+
hasDateFields,
|
|
5740
|
+
hasCategoricalFields,
|
|
5741
|
+
isProductLike,
|
|
5742
|
+
numericFieldCount,
|
|
5743
|
+
categoricalFieldCount
|
|
5744
|
+
};
|
|
5745
|
+
}
|
|
5746
|
+
// ─── Intent Heuristic Checkers ─────────────────────────────────────────────
|
|
5747
|
+
static isInformationLookup(query) {
|
|
5748
|
+
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)) {
|
|
5749
|
+
if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top selling|best)\b/i.test(query)) {
|
|
5750
|
+
return true;
|
|
5751
|
+
}
|
|
5752
|
+
}
|
|
5753
|
+
if (/^(what is the )?(price|cost|fee|rate) of\b/i.test(query)) {
|
|
5754
|
+
return true;
|
|
5755
|
+
}
|
|
5756
|
+
return false;
|
|
5757
|
+
}
|
|
5758
|
+
static isComparisonQuery(query) {
|
|
5759
|
+
return /\b(compare|comparison|vs\.?|versus|difference between|differ|contrast|against)\b/i.test(query);
|
|
5760
|
+
}
|
|
5761
|
+
static isProductQuery(query) {
|
|
5762
|
+
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);
|
|
5763
|
+
}
|
|
5764
|
+
static isRecommendationQuery(query) {
|
|
5765
|
+
return /\b(recommend|suggest|what should i (buy|get|choose)|best options|top picks)\b/i.test(query);
|
|
5766
|
+
}
|
|
5767
|
+
static isTrendQuery(query) {
|
|
5768
|
+
return /\b(trend|trends|growth|over time|historical|timeline|monthly|yearly|weekly|daily|revenue growth|sales growth)\b/i.test(query);
|
|
5769
|
+
}
|
|
5770
|
+
static isRankingQuery(query) {
|
|
5771
|
+
return /\b(highest|lowest|top\s*\d+|bottom\s*\d+|rank|ranking|top performing|best performing)\b/i.test(query);
|
|
5772
|
+
}
|
|
5773
|
+
static isAnalyticsQuery(query) {
|
|
5774
|
+
return /\b(sales|revenue|analytics|insights|distribution|percentage|share|breakdown|breakup|scatter|correlation|metrics|performance|by region|by category)\b/i.test(query);
|
|
5775
|
+
}
|
|
5776
|
+
};
|
|
5777
|
+
|
|
5778
|
+
// src/rendering/rules/Rule1SpecificInfoRule.ts
|
|
5779
|
+
var Rule1SpecificInfoRule = class {
|
|
5780
|
+
constructor() {
|
|
5781
|
+
this.name = "Rule1SpecificInfo";
|
|
5782
|
+
this.priority = 100;
|
|
5783
|
+
}
|
|
5784
|
+
evaluate(context, intent) {
|
|
5785
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
5786
|
+
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);
|
|
5787
|
+
const isNonVisual = !/\b(compare|versus|vs\.?|trend|monthly|revenue|best|top 10|chart|table|list|grid)\b/i.test(q);
|
|
5788
|
+
if (isFactQuery && isNonVisual) {
|
|
5789
|
+
return {
|
|
5790
|
+
renderType: "text",
|
|
5791
|
+
confidence: 0.95,
|
|
5792
|
+
reason: "Rule 1: Specific information request (fact, explanation, or definition) requires plain text rendering."
|
|
5793
|
+
};
|
|
5794
|
+
}
|
|
5795
|
+
return null;
|
|
5796
|
+
}
|
|
5797
|
+
};
|
|
5798
|
+
|
|
5799
|
+
// src/rendering/rules/Rule2ComparisonRule.ts
|
|
5800
|
+
var Rule2ComparisonRule = class {
|
|
5801
|
+
constructor() {
|
|
5802
|
+
this.name = "Rule2Comparison";
|
|
5803
|
+
this.priority = 90;
|
|
5804
|
+
}
|
|
5805
|
+
evaluate(context, intent) {
|
|
5806
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
5807
|
+
const isComparison = intent === "comparison" || /\b(compare|comparison|vs\.?|versus|difference between|differ|contrast|against)\b/i.test(q);
|
|
5808
|
+
if (isComparison) {
|
|
5809
|
+
return {
|
|
5810
|
+
renderType: "table",
|
|
5811
|
+
confidence: 0.9,
|
|
5812
|
+
reason: "Rule 2: Comparison query contrasting multiple entities requires structured table rendering."
|
|
5813
|
+
};
|
|
5814
|
+
}
|
|
5815
|
+
return null;
|
|
5816
|
+
}
|
|
5817
|
+
};
|
|
5818
|
+
|
|
5819
|
+
// src/rendering/rules/Rule3ProductDiscoveryRule.ts
|
|
5820
|
+
var Rule3ProductDiscoveryRule = class {
|
|
5821
|
+
constructor() {
|
|
5822
|
+
this.name = "Rule3ProductDiscovery";
|
|
5823
|
+
this.priority = 85;
|
|
5824
|
+
}
|
|
5825
|
+
evaluate(context, intent) {
|
|
5826
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
5827
|
+
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);
|
|
5828
|
+
const isNotChart = !/\b(chart|sales|revenue|monthly|growth|trend|over time)\b/i.test(q);
|
|
5829
|
+
const isAnalyticalRanking = /\b(top performing|top selling|best performing|performing|sales|revenue|ranking)\b/i.test(q);
|
|
5830
|
+
if (isDiscovery && isNotChart && !isAnalyticalRanking) {
|
|
5831
|
+
return {
|
|
5832
|
+
renderType: "carousel",
|
|
5833
|
+
confidence: 0.9,
|
|
5834
|
+
reason: "Rule 3: Product discovery/recommendation query requires product carousel rendering."
|
|
5835
|
+
};
|
|
5836
|
+
}
|
|
5837
|
+
return null;
|
|
5838
|
+
}
|
|
5839
|
+
};
|
|
5840
|
+
|
|
5841
|
+
// src/rendering/rules/Rule4AnalyticalRule.ts
|
|
5842
|
+
var Rule4AnalyticalRule = class {
|
|
5843
|
+
constructor() {
|
|
5844
|
+
this.name = "Rule4Analytical";
|
|
5845
|
+
this.priority = 80;
|
|
5846
|
+
}
|
|
5847
|
+
evaluate(context, intent) {
|
|
5848
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
5849
|
+
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);
|
|
5850
|
+
if (!isAnalytical) return null;
|
|
5851
|
+
let chartType = "bar";
|
|
5852
|
+
if (intent === "trend_analysis" || /\b(monthly|yearly|weekly|growth|over time|trend|timeline|historical)\b/i.test(q)) {
|
|
5853
|
+
chartType = "line";
|
|
5854
|
+
} else if (/\b(share|percentage|percent|breakup|breakdown|composition|pie|donut|proportion)\b/i.test(q)) {
|
|
5855
|
+
chartType = "pie";
|
|
5856
|
+
} else if (/\b(correlation|scatter|relationship|relation|impact of|depends on)\b/i.test(q)) {
|
|
5857
|
+
chartType = "scatter";
|
|
5858
|
+
} else {
|
|
5859
|
+
chartType = "bar";
|
|
5860
|
+
}
|
|
5861
|
+
return {
|
|
5862
|
+
renderType: "chart",
|
|
5863
|
+
chartType,
|
|
5864
|
+
confidence: 0.85,
|
|
5865
|
+
reason: `Rule 4: Analytical query with numerical insights selects ${chartType} chart.`
|
|
5866
|
+
};
|
|
5867
|
+
}
|
|
5868
|
+
};
|
|
5869
|
+
|
|
5870
|
+
// src/rendering/rules/Rule5MixedResponseRule.ts
|
|
5871
|
+
var Rule5MixedResponseRule = class {
|
|
5872
|
+
constructor() {
|
|
5873
|
+
this.name = "Rule5MixedResponse";
|
|
5874
|
+
this.priority = 95;
|
|
5875
|
+
}
|
|
5876
|
+
evaluate(context, _intent) {
|
|
5877
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
5878
|
+
const isTopSellingThisMonth = /\btop\s*(selling|performing)?\s*products?\s*(this|last)?\s*(month|year|week)\b/i.test(q);
|
|
5879
|
+
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);
|
|
5880
|
+
if (isTopSellingThisMonth || hasMultipleSectionsRequest) {
|
|
5881
|
+
return {
|
|
5882
|
+
renderType: "mixed",
|
|
5883
|
+
confidence: 0.95,
|
|
5884
|
+
reason: "Rule 5: Mixed response query requires multi-section composition (text, carousel, chart).",
|
|
5885
|
+
sections: [
|
|
5886
|
+
{ type: "text", title: "Summary" },
|
|
5887
|
+
{ type: "carousel", title: "Top Products" },
|
|
5888
|
+
{ type: "chart", chartType: "bar", title: "Sales Performance" }
|
|
5889
|
+
]
|
|
5890
|
+
};
|
|
5891
|
+
}
|
|
5892
|
+
return null;
|
|
5893
|
+
}
|
|
5894
|
+
};
|
|
5895
|
+
|
|
5896
|
+
// src/rendering/rules/Rule6SmallResultSetRule.ts
|
|
5897
|
+
var Rule6SmallResultSetRule = class {
|
|
5898
|
+
constructor() {
|
|
5899
|
+
this.name = "Rule6SmallResultSet";
|
|
5900
|
+
this.priority = 110;
|
|
5901
|
+
}
|
|
5902
|
+
evaluate(context, _intent) {
|
|
5903
|
+
const docs = context.retrievedDocuments || [];
|
|
5904
|
+
if (docs.length > 0 && docs.length < 3) {
|
|
5905
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
5906
|
+
const isExplicitComparison = /\b(compare|versus|vs\.?)\b/i.test(q);
|
|
5907
|
+
return {
|
|
5908
|
+
renderType: isExplicitComparison ? "table" : "text",
|
|
5909
|
+
confidence: 0.98,
|
|
5910
|
+
reason: `Rule 6: Small result set (${docs.length} rows < 3). Charts avoided to prevent sparse or uninformative visualizations.`
|
|
5911
|
+
};
|
|
5912
|
+
}
|
|
5913
|
+
return null;
|
|
5914
|
+
}
|
|
5915
|
+
};
|
|
5916
|
+
|
|
5917
|
+
// src/rendering/rules/Rule7LargeTableRule.ts
|
|
5918
|
+
var Rule7LargeTableRule = class {
|
|
5919
|
+
constructor() {
|
|
5920
|
+
this.name = "Rule7LargeTable";
|
|
5921
|
+
this.priority = 75;
|
|
5922
|
+
}
|
|
5923
|
+
evaluate(context, intent) {
|
|
5924
|
+
const docs = context.retrievedDocuments || [];
|
|
5925
|
+
if (docs.length > 5) {
|
|
5926
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
5927
|
+
const hasNumericAgg = intent === "analytics" || intent === "ranking" || /\b(sales|revenue|growth|count|total|average)\b/i.test(q);
|
|
5928
|
+
if (hasNumericAgg) {
|
|
5929
|
+
return {
|
|
5930
|
+
renderType: "mixed",
|
|
5931
|
+
confidence: 0.88,
|
|
5932
|
+
reason: `Rule 7: Large result set (${docs.length} rows > 5) with numeric aggregation rendered as Table + Chart.`,
|
|
5933
|
+
sections: [
|
|
5934
|
+
{ type: "table", title: "Detailed Data Table" },
|
|
5935
|
+
{ type: "chart", chartType: "bar", title: "Numerical Breakdown" }
|
|
5936
|
+
]
|
|
5937
|
+
};
|
|
5938
|
+
}
|
|
5939
|
+
return {
|
|
5940
|
+
renderType: "table",
|
|
5941
|
+
confidence: 0.85,
|
|
5942
|
+
reason: `Rule 7: Large result set (${docs.length} rows > 5) rendered as structured table for readability.`
|
|
5943
|
+
};
|
|
5944
|
+
}
|
|
5945
|
+
return null;
|
|
5946
|
+
}
|
|
5947
|
+
};
|
|
5948
|
+
|
|
5949
|
+
// src/rendering/RuleEngine.ts
|
|
5950
|
+
var RuleEngine = class {
|
|
5951
|
+
constructor() {
|
|
5952
|
+
this.rules = [];
|
|
5953
|
+
this.registerDefaultRules();
|
|
5954
|
+
}
|
|
5955
|
+
/**
|
|
5956
|
+
* Register standard rules in priority order.
|
|
5957
|
+
*/
|
|
5958
|
+
registerDefaultRules() {
|
|
5959
|
+
this.registerRule(new Rule6SmallResultSetRule());
|
|
5960
|
+
this.registerRule(new Rule1SpecificInfoRule());
|
|
5961
|
+
this.registerRule(new Rule5MixedResponseRule());
|
|
5962
|
+
this.registerRule(new Rule2ComparisonRule());
|
|
5963
|
+
this.registerRule(new Rule3ProductDiscoveryRule());
|
|
5964
|
+
this.registerRule(new Rule4AnalyticalRule());
|
|
5965
|
+
this.registerRule(new Rule7LargeTableRule());
|
|
5966
|
+
}
|
|
5967
|
+
/**
|
|
5968
|
+
* Add or replace a rule in the engine.
|
|
5969
|
+
* Automatically keeps rules sorted by descending priority.
|
|
5970
|
+
*/
|
|
5971
|
+
registerRule(rule) {
|
|
5972
|
+
this.rules = this.rules.filter((r) => r.name !== rule.name);
|
|
5973
|
+
this.rules.push(rule);
|
|
5974
|
+
this.rules.sort((a, b) => b.priority - a.priority);
|
|
5975
|
+
}
|
|
5976
|
+
/**
|
|
5977
|
+
* Evaluate context against rules in descending priority order.
|
|
5978
|
+
* Returns the first non-null RenderDecision.
|
|
5979
|
+
*/
|
|
5980
|
+
evaluate(context, intent) {
|
|
5981
|
+
for (const rule of this.rules) {
|
|
5982
|
+
const decision = rule.evaluate(context, intent);
|
|
5983
|
+
if (decision) {
|
|
5984
|
+
return decision;
|
|
5985
|
+
}
|
|
5986
|
+
}
|
|
5987
|
+
return {
|
|
5988
|
+
renderType: "text",
|
|
5989
|
+
confidence: 0.7,
|
|
5990
|
+
reason: "Default fallback: No specific visualization rule matched context."
|
|
5991
|
+
};
|
|
5992
|
+
}
|
|
5993
|
+
/**
|
|
5994
|
+
* List all registered rules.
|
|
5995
|
+
*/
|
|
5996
|
+
getRegisteredRules() {
|
|
5997
|
+
return this.rules.map((r) => ({ name: r.name, priority: r.priority }));
|
|
5998
|
+
}
|
|
5999
|
+
};
|
|
6000
|
+
|
|
6001
|
+
// src/rendering/RendererRegistry.ts
|
|
6002
|
+
var TextRendererStrategy = class {
|
|
6003
|
+
constructor() {
|
|
6004
|
+
this.type = "text";
|
|
6005
|
+
}
|
|
6006
|
+
render(data, options) {
|
|
6007
|
+
const title = (options == null ? void 0 : options.title) || "Information";
|
|
6008
|
+
if (typeof data === "string") {
|
|
6009
|
+
return { type: "text", title, data: { content: data } };
|
|
6010
|
+
}
|
|
6011
|
+
const docs = Array.isArray(data) ? data : [];
|
|
6012
|
+
const text = docs.map((d) => d.content).join("\n\n");
|
|
6013
|
+
return { type: "text", title, data: { content: text || "No detailed content available." } };
|
|
6014
|
+
}
|
|
6015
|
+
};
|
|
6016
|
+
var TableRendererStrategy = class {
|
|
6017
|
+
constructor() {
|
|
6018
|
+
this.type = "table";
|
|
6019
|
+
}
|
|
6020
|
+
render(data, options) {
|
|
6021
|
+
const docs = Array.isArray(data) ? data : [];
|
|
6022
|
+
const query = (options == null ? void 0 : options.query) || "";
|
|
6023
|
+
return UITransformer.transform(query, docs, void 0, void 0, {
|
|
6024
|
+
visualizationHint: "table",
|
|
6025
|
+
recommendedChart: "table",
|
|
6026
|
+
filterInStockOnly: false,
|
|
6027
|
+
wantsExplicitTable: true,
|
|
6028
|
+
isTemporal: false,
|
|
6029
|
+
isComparison: true,
|
|
6030
|
+
language: "en"
|
|
6031
|
+
});
|
|
6032
|
+
}
|
|
6033
|
+
};
|
|
6034
|
+
var CarouselRendererStrategy = class {
|
|
6035
|
+
constructor() {
|
|
6036
|
+
this.type = "carousel";
|
|
6037
|
+
}
|
|
6038
|
+
render(data, options) {
|
|
6039
|
+
const docs = Array.isArray(data) ? data : [];
|
|
6040
|
+
const query = (options == null ? void 0 : options.query) || "";
|
|
6041
|
+
return UITransformer.transform(query, docs, void 0, void 0, {
|
|
6042
|
+
visualizationHint: "product_browse",
|
|
6043
|
+
recommendedChart: "text",
|
|
6044
|
+
filterInStockOnly: false,
|
|
6045
|
+
wantsExplicitTable: false,
|
|
6046
|
+
isTemporal: false,
|
|
6047
|
+
isComparison: false,
|
|
6048
|
+
language: "en"
|
|
6049
|
+
});
|
|
6050
|
+
}
|
|
6051
|
+
};
|
|
6052
|
+
var ChartRendererStrategy = class {
|
|
6053
|
+
constructor() {
|
|
6054
|
+
this.type = "chart";
|
|
6055
|
+
}
|
|
6056
|
+
render(data, options) {
|
|
6057
|
+
const docs = Array.isArray(data) ? data : [];
|
|
6058
|
+
const query = (options == null ? void 0 : options.query) || "";
|
|
6059
|
+
const chartType = (options == null ? void 0 : options.chartType) || "bar";
|
|
6060
|
+
let hint = "comparison";
|
|
6061
|
+
let rec = "bar_chart";
|
|
6062
|
+
if (chartType === "line") {
|
|
6063
|
+
hint = "trend";
|
|
6064
|
+
rec = "line_chart";
|
|
6065
|
+
} else if (chartType === "pie") {
|
|
6066
|
+
hint = "composition";
|
|
6067
|
+
rec = "pie_chart";
|
|
6068
|
+
} else if (chartType === "scatter") {
|
|
6069
|
+
hint = "correlation";
|
|
6070
|
+
rec = "scatter_plot";
|
|
6071
|
+
}
|
|
6072
|
+
return UITransformer.transform(query, docs, void 0, void 0, {
|
|
6073
|
+
visualizationHint: hint,
|
|
6074
|
+
recommendedChart: rec,
|
|
6075
|
+
filterInStockOnly: false,
|
|
6076
|
+
wantsExplicitTable: false,
|
|
6077
|
+
isTemporal: chartType === "line",
|
|
6078
|
+
isComparison: chartType === "bar",
|
|
6079
|
+
language: "en"
|
|
6080
|
+
});
|
|
6081
|
+
}
|
|
6082
|
+
};
|
|
6083
|
+
var MixedRendererStrategy = class {
|
|
6084
|
+
constructor() {
|
|
6085
|
+
this.type = "mixed";
|
|
6086
|
+
}
|
|
6087
|
+
render(data, options) {
|
|
6088
|
+
const docs = Array.isArray(data) ? data : [];
|
|
6089
|
+
const sections = (options == null ? void 0 : options.sections) || [];
|
|
6090
|
+
const sectionPayloads = sections.map((s) => {
|
|
6091
|
+
const strategy = RendererRegistry.getStrategy(s.type);
|
|
6092
|
+
return {
|
|
6093
|
+
type: s.type,
|
|
6094
|
+
title: s.title,
|
|
6095
|
+
payload: strategy.render(data, __spreadProps(__spreadValues({}, options), { chartType: s.chartType, title: s.title }))
|
|
6096
|
+
};
|
|
6097
|
+
});
|
|
6098
|
+
return {
|
|
6099
|
+
type: "table",
|
|
6100
|
+
// Fallback container type for compatibility
|
|
6101
|
+
title: (options == null ? void 0 : options.title) || "Composite Response",
|
|
6102
|
+
description: `Mixed payload containing ${sectionPayloads.length} visual sections`,
|
|
6103
|
+
data: { sections: sectionPayloads }
|
|
6104
|
+
};
|
|
6105
|
+
}
|
|
6106
|
+
};
|
|
6107
|
+
var _RendererRegistry = class _RendererRegistry {
|
|
6108
|
+
/**
|
|
6109
|
+
* Register a new renderer strategy.
|
|
6110
|
+
* Enables seamless addition of custom visualizers (Timeline, Maps, Flow Diagrams, etc.).
|
|
6111
|
+
*/
|
|
6112
|
+
static registerStrategy(strategy) {
|
|
6113
|
+
this.strategies.set(String(strategy.type).toLowerCase(), strategy);
|
|
6114
|
+
}
|
|
6115
|
+
/**
|
|
6116
|
+
* Retrieve a registered renderer strategy by type string.
|
|
6117
|
+
*/
|
|
6118
|
+
static getStrategy(type) {
|
|
6119
|
+
const key = type.toLowerCase();
|
|
6120
|
+
const strategy = this.strategies.get(key);
|
|
6121
|
+
if (!strategy) {
|
|
6122
|
+
return this.strategies.get("text");
|
|
6123
|
+
}
|
|
6124
|
+
return strategy;
|
|
6125
|
+
}
|
|
6126
|
+
/**
|
|
6127
|
+
* Check if a renderer strategy is registered.
|
|
6128
|
+
*/
|
|
6129
|
+
static hasStrategy(type) {
|
|
6130
|
+
return this.strategies.has(type.toLowerCase());
|
|
6131
|
+
}
|
|
6132
|
+
/**
|
|
6133
|
+
* List all registered strategy types.
|
|
6134
|
+
*/
|
|
6135
|
+
static getRegisteredTypes() {
|
|
6136
|
+
return Array.from(this.strategies.keys());
|
|
6137
|
+
}
|
|
6138
|
+
};
|
|
6139
|
+
_RendererRegistry.strategies = /* @__PURE__ */ new Map();
|
|
6140
|
+
_RendererRegistry.registerStrategy(new TextRendererStrategy());
|
|
6141
|
+
_RendererRegistry.registerStrategy(new TableRendererStrategy());
|
|
6142
|
+
_RendererRegistry.registerStrategy(new CarouselRendererStrategy());
|
|
6143
|
+
_RendererRegistry.registerStrategy(new ChartRendererStrategy());
|
|
6144
|
+
_RendererRegistry.registerStrategy(new MixedRendererStrategy());
|
|
6145
|
+
var RendererRegistry = _RendererRegistry;
|
|
6146
|
+
|
|
6147
|
+
// src/rendering/VisualizationDecisionEngine.ts
|
|
6148
|
+
var LRUDecisionCache = class {
|
|
6149
|
+
constructor(maxSize = 200) {
|
|
6150
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
6151
|
+
this.maxSize = maxSize;
|
|
6152
|
+
}
|
|
6153
|
+
generateKey(context) {
|
|
6154
|
+
var _a2, _b;
|
|
6155
|
+
const q = (context.userQuery || "").toLowerCase().trim();
|
|
6156
|
+
const docCount = (_b = (_a2 = context.retrievedDocuments) == null ? void 0 : _a2.length) != null ? _b : 0;
|
|
6157
|
+
return `${q}::docs:${docCount}`;
|
|
6158
|
+
}
|
|
6159
|
+
get(context) {
|
|
6160
|
+
const key = this.generateKey(context);
|
|
6161
|
+
const item = this.cache.get(key);
|
|
6162
|
+
if (item) {
|
|
6163
|
+
this.cache.delete(key);
|
|
6164
|
+
this.cache.set(key, item);
|
|
6165
|
+
return item.decision;
|
|
6166
|
+
}
|
|
6167
|
+
return void 0;
|
|
6168
|
+
}
|
|
6169
|
+
set(context, decision) {
|
|
6170
|
+
const key = this.generateKey(context);
|
|
6171
|
+
if (this.cache.has(key)) {
|
|
6172
|
+
this.cache.delete(key);
|
|
6173
|
+
} else if (this.cache.size >= this.maxSize) {
|
|
6174
|
+
const oldest = this.cache.keys().next().value;
|
|
6175
|
+
if (oldest !== void 0) this.cache.delete(oldest);
|
|
6176
|
+
}
|
|
6177
|
+
this.cache.set(key, { decision, timestamp: Date.now() });
|
|
6178
|
+
}
|
|
6179
|
+
clear() {
|
|
6180
|
+
this.cache.clear();
|
|
6181
|
+
}
|
|
6182
|
+
get size() {
|
|
6183
|
+
return this.cache.size;
|
|
6184
|
+
}
|
|
6185
|
+
};
|
|
6186
|
+
var VisualizationDecisionEngine = class {
|
|
6187
|
+
/**
|
|
6188
|
+
* Evaluate user query, documents, and context to produce a RenderDecision.
|
|
6189
|
+
* Execution time is < 1ms for cached or rule-matched queries.
|
|
6190
|
+
*/
|
|
6191
|
+
static decideVisualization(userQuery, retrievedDocuments, llmResponse, metadata) {
|
|
6192
|
+
const context = {
|
|
6193
|
+
userQuery,
|
|
6194
|
+
retrievedDocuments,
|
|
6195
|
+
llmResponse,
|
|
6196
|
+
metadata
|
|
6197
|
+
};
|
|
6198
|
+
const cached = this.cache.get(context);
|
|
6199
|
+
if (cached) {
|
|
6200
|
+
return cached;
|
|
6201
|
+
}
|
|
6202
|
+
const { intent } = IntentClassifier.classify(context);
|
|
6203
|
+
const decision = this.ruleEngine.evaluate(context, intent);
|
|
6204
|
+
this.cache.set(context, decision);
|
|
6205
|
+
return decision;
|
|
6206
|
+
}
|
|
6207
|
+
/**
|
|
6208
|
+
* Convenience helper to decide AND render the data into a UITransformationResponse.
|
|
6209
|
+
*/
|
|
6210
|
+
static render(userQuery, retrievedDocuments, llmResponse, metadata) {
|
|
6211
|
+
const decision = this.decideVisualization(userQuery, retrievedDocuments, llmResponse, metadata);
|
|
6212
|
+
const strategy = RendererRegistry.getStrategy(decision.renderType);
|
|
6213
|
+
return strategy.render(retrievedDocuments || [], __spreadValues({
|
|
6214
|
+
query: userQuery,
|
|
6215
|
+
chartType: decision.chartType,
|
|
6216
|
+
sections: decision.sections
|
|
6217
|
+
}, metadata));
|
|
6218
|
+
}
|
|
6219
|
+
/**
|
|
6220
|
+
* Register a custom rule in the rule engine.
|
|
6221
|
+
*/
|
|
6222
|
+
static registerRule(rule) {
|
|
6223
|
+
this.ruleEngine.registerRule(rule);
|
|
6224
|
+
this.cache.clear();
|
|
6225
|
+
}
|
|
6226
|
+
/**
|
|
6227
|
+
* Register a custom renderer strategy.
|
|
6228
|
+
*/
|
|
6229
|
+
static registerRendererStrategy(strategy) {
|
|
6230
|
+
RendererRegistry.registerStrategy(strategy);
|
|
6231
|
+
}
|
|
6232
|
+
/**
|
|
6233
|
+
* Clear the decision cache.
|
|
6234
|
+
*/
|
|
6235
|
+
static clearCache() {
|
|
6236
|
+
this.cache.clear();
|
|
6237
|
+
}
|
|
6238
|
+
/**
|
|
6239
|
+
* Get size of current decision cache.
|
|
6240
|
+
*/
|
|
6241
|
+
static getCacheSize() {
|
|
6242
|
+
return this.cache.size;
|
|
6243
|
+
}
|
|
6244
|
+
};
|
|
6245
|
+
VisualizationDecisionEngine.ruleEngine = new RuleEngine();
|
|
6246
|
+
VisualizationDecisionEngine.cache = new LRUDecisionCache(200);
|
|
6247
|
+
|
|
6248
|
+
// src/utils/UITransformer.ts
|
|
5657
6249
|
var UITransformer = class _UITransformer {
|
|
5658
6250
|
// ─── Public Entry Points ─────────────────────────────────────────────────
|
|
5659
6251
|
/**
|
|
@@ -5700,6 +6292,9 @@ var UITransformer = class _UITransformer {
|
|
|
5700
6292
|
return this.hasMultipleFields(filteredData) ? this.transformToTable(filteredData, userQuery) : this.transformToText(filteredData);
|
|
5701
6293
|
}
|
|
5702
6294
|
if ((hasProducts || this.isProductQuery(userQuery)) && resolvedIntent.visualizationHint === "product_browse") {
|
|
6295
|
+
if (!this.isProductQuery(userQuery)) {
|
|
6296
|
+
return this.transformToText(filteredData);
|
|
6297
|
+
}
|
|
5703
6298
|
return this.transformToProductCarousel(filteredData, config, trainedSchema);
|
|
5704
6299
|
}
|
|
5705
6300
|
const automatic = this.chooseAutomaticVisualization(filteredData, profile, userQuery);
|
|
@@ -5714,6 +6309,11 @@ var UITransformer = class _UITransformer {
|
|
|
5714
6309
|
* Step 3 — Fall back to the heuristic `transform()` if either LLM call fails.
|
|
5715
6310
|
*/
|
|
5716
6311
|
static async analyzeAndDecide(query, sources, llm) {
|
|
6312
|
+
const decision = VisualizationDecisionEngine.decideVisualization(query, sources);
|
|
6313
|
+
if (decision.renderType === "text") {
|
|
6314
|
+
console.debug("[UITransformer] Decision Engine selected plain text rendering \u2014 skipping visual LLM calls.");
|
|
6315
|
+
return this.transformToText(sources);
|
|
6316
|
+
}
|
|
5717
6317
|
let intent;
|
|
5718
6318
|
try {
|
|
5719
6319
|
intent = await this.detectIntent(query, llm, sources);
|
|
@@ -6163,7 +6763,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
6163
6763
|
}
|
|
6164
6764
|
static transformToText(data) {
|
|
6165
6765
|
return this.createTextResponse(
|
|
6166
|
-
"
|
|
6766
|
+
"Retrieved Context",
|
|
6167
6767
|
data.map((item) => item.content).join("\n\n"),
|
|
6168
6768
|
`Found ${data.length} relevant results`
|
|
6169
6769
|
);
|
|
@@ -6330,16 +6930,13 @@ ${schemaProfileText}` : ""}`;
|
|
|
6330
6930
|
"product",
|
|
6331
6931
|
"price",
|
|
6332
6932
|
"stock",
|
|
6333
|
-
"item",
|
|
6334
6933
|
"sku",
|
|
6335
6934
|
"brand",
|
|
6336
|
-
"model",
|
|
6337
6935
|
"msrp",
|
|
6338
6936
|
"inventory",
|
|
6339
6937
|
"buy",
|
|
6340
6938
|
"shop",
|
|
6341
6939
|
"beauty",
|
|
6342
|
-
"care",
|
|
6343
6940
|
"cosmetic",
|
|
6344
6941
|
"facial",
|
|
6345
6942
|
"cream",
|
|
@@ -6352,7 +6949,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
6352
6949
|
const metadata = item.metadata || {};
|
|
6353
6950
|
const hasMetadataKey = Object.keys(metadata).some((k) => {
|
|
6354
6951
|
const val = metadata[k];
|
|
6355
|
-
return ["price", "product", "sku", "brand", "
|
|
6952
|
+
return ["price", "product", "sku", "brand", "cost"].includes(k.toLowerCase()) && val !== null && val !== void 0 && val !== "";
|
|
6356
6953
|
});
|
|
6357
6954
|
const hasPricePattern = /\$\s*\d+\.\d{2}/.test(content);
|
|
6358
6955
|
return hasKeywords || hasMetadataKey || hasPricePattern;
|
|
@@ -7041,6 +7638,13 @@ SchemaMapper.TARGET_PROPERTIES = {
|
|
|
7041
7638
|
};
|
|
7042
7639
|
|
|
7043
7640
|
// src/core/Pipeline.ts
|
|
7641
|
+
function formatNamespace(raw) {
|
|
7642
|
+
if (!raw || !raw.trim()) return "retrivora-default";
|
|
7643
|
+
const trimmed = raw.trim();
|
|
7644
|
+
if (trimmed.startsWith("retrivora-")) return trimmed;
|
|
7645
|
+
const sanitized = trimmed.toLowerCase().replace(/[^a-z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
7646
|
+
return `retrivora-${sanitized}`;
|
|
7647
|
+
}
|
|
7044
7648
|
var LRUEmbeddingCache = class {
|
|
7045
7649
|
constructor(maxSize = 500) {
|
|
7046
7650
|
this.cache = /* @__PURE__ */ new Map();
|
|
@@ -7232,7 +7836,7 @@ ${m.content}`).join("\n\n---\n\n");
|
|
|
7232
7836
|
*/
|
|
7233
7837
|
async ingest(documents, namespace) {
|
|
7234
7838
|
await this.initialize();
|
|
7235
|
-
const ns = namespace != null ? namespace : this.config.projectId;
|
|
7839
|
+
const ns = formatNamespace(namespace != null ? namespace : this.config.projectId);
|
|
7236
7840
|
const results = [];
|
|
7237
7841
|
for (const doc of documents) {
|
|
7238
7842
|
try {
|
|
@@ -7451,7 +8055,7 @@ ${m.content}`).join("\n\n---\n\n");
|
|
|
7451
8055
|
return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
|
|
7452
8056
|
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;
|
|
7453
8057
|
yield new __await(this.initialize());
|
|
7454
|
-
const ns = namespace != null ? namespace : this.config.projectId;
|
|
8058
|
+
const ns = formatNamespace(namespace != null ? namespace : this.config.projectId);
|
|
7455
8059
|
const topK = (_b = (_a2 = this.config.rag) == null ? void 0 : _a2.topK) != null ? _b : 5;
|
|
7456
8060
|
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0.3;
|
|
7457
8061
|
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -7589,7 +8193,7 @@ ${context}`;
|
|
|
7589
8193
|
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);
|
|
7590
8194
|
const modelNameLower = this.config.llm.model.toLowerCase();
|
|
7591
8195
|
const isReasoningModel = modelNameLower.includes("-r1") || modelNameLower.includes("deepseek-r1") || modelNameLower.includes("reasoning") || modelNameLower.includes("thinking");
|
|
7592
|
-
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" ||
|
|
8196
|
+
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);
|
|
7593
8197
|
let finalRestrictionSuffix = restrictionSuffix;
|
|
7594
8198
|
if (isSimulatedThinking) {
|
|
7595
8199
|
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.`)";
|
|
@@ -7915,7 +8519,7 @@ ${context}`;
|
|
|
7915
8519
|
}
|
|
7916
8520
|
async retrieve(query, options) {
|
|
7917
8521
|
var _a2, _b, _c, _d, _e, _f, _g2;
|
|
7918
|
-
const ns = (_a2 = options.namespace) != null ? _a2 : this.config.projectId;
|
|
8522
|
+
const ns = formatNamespace((_a2 = options.namespace) != null ? _a2 : this.config.projectId);
|
|
7919
8523
|
const topK = (_b = options.topK) != null ? _b : 5;
|
|
7920
8524
|
const cacheKey = `${ns}::${query}`;
|
|
7921
8525
|
let queryVector = this.embeddingCache.get(cacheKey);
|
|
@@ -7984,7 +8588,7 @@ Optimized Search Query:`;
|
|
|
7984
8588
|
async getSuggestions(query, namespace) {
|
|
7985
8589
|
if (!query || query.trim().length < 3) return [];
|
|
7986
8590
|
await this.initialize();
|
|
7987
|
-
const ns = namespace != null ? namespace : this.config.projectId;
|
|
8591
|
+
const ns = formatNamespace(namespace != null ? namespace : this.config.projectId);
|
|
7988
8592
|
try {
|
|
7989
8593
|
const { sources } = await this.retrieve(query, { namespace: ns, topK: 5 });
|
|
7990
8594
|
if (sources.length === 0) return [];
|
|
@@ -8217,7 +8821,7 @@ var LicenseVerifier = class {
|
|
|
8217
8821
|
`The database provider "${provider}" is not allowed on the Hobby tier. Hobby tier is restricted to PostgreSQL and Supabase. Please upgrade to a Developer Pro or Enterprise plan.`
|
|
8218
8822
|
);
|
|
8219
8823
|
}
|
|
8220
|
-
} else if (tier === "pro" || tier === "developer_pro" || tier === "premium" || tier === "growth") {
|
|
8824
|
+
} else if (tier === "pro" || tier === "developer_pro" || tier === "premium" || tier === "growth" || tier === "free_trial" || tier === "free_tier" || tier === "free") {
|
|
8221
8825
|
const allowedPro = [
|
|
8222
8826
|
"postgresql",
|
|
8223
8827
|
"pgvector",
|
|
@@ -8476,7 +9080,7 @@ var DocumentParser = class {
|
|
|
8476
9080
|
*/
|
|
8477
9081
|
static async parse(file, fileName, mimeType) {
|
|
8478
9082
|
var _a2;
|
|
8479
|
-
const extension = (_a2 = fileName.split(".").pop()) == null ? void 0 : _a2.toLowerCase();
|
|
9083
|
+
const extension = ((_a2 = fileName.split(".").pop()) == null ? void 0 : _a2.toLowerCase()) || "";
|
|
8480
9084
|
if (extension === "txt" || extension === "md" || mimeType === "text/plain" || mimeType === "text/markdown") {
|
|
8481
9085
|
return this.readAsText(file);
|
|
8482
9086
|
}
|
|
@@ -8492,6 +9096,29 @@ var DocumentParser = class {
|
|
|
8492
9096
|
if (extension === "csv" || mimeType === "text/csv") {
|
|
8493
9097
|
return this.readAsText(file);
|
|
8494
9098
|
}
|
|
9099
|
+
if (extension === "xlsx" || extension === "xls" || mimeType.includes("spreadsheet") || mimeType.includes("excel")) {
|
|
9100
|
+
try {
|
|
9101
|
+
const xlsx = await import("xlsx");
|
|
9102
|
+
const buffer = Buffer.isBuffer(file) ? file : Buffer.from(await file.arrayBuffer());
|
|
9103
|
+
const workbook = xlsx.read(buffer, { type: "buffer" });
|
|
9104
|
+
const sheetsText = [];
|
|
9105
|
+
for (const sheetName of workbook.SheetNames) {
|
|
9106
|
+
const sheet = workbook.Sheets[sheetName];
|
|
9107
|
+
const csv = xlsx.utils.sheet_to_csv(sheet);
|
|
9108
|
+
if (csv && csv.trim()) {
|
|
9109
|
+
sheetsText.push(`--- Sheet: ${sheetName} ---
|
|
9110
|
+
${csv.trim()}`);
|
|
9111
|
+
}
|
|
9112
|
+
}
|
|
9113
|
+
return sheetsText.join("\n\n") || `[Empty Excel Workbook: ${fileName}]`;
|
|
9114
|
+
} catch (e) {
|
|
9115
|
+
try {
|
|
9116
|
+
return await this.readAsText(file);
|
|
9117
|
+
} catch (e2) {
|
|
9118
|
+
return `[Excel File Content: ${fileName}]`;
|
|
9119
|
+
}
|
|
9120
|
+
}
|
|
9121
|
+
}
|
|
8495
9122
|
if (extension === "pdf" || mimeType === "application/pdf") {
|
|
8496
9123
|
try {
|
|
8497
9124
|
const pdf = await import("pdf-parse");
|
|
@@ -8503,15 +9130,15 @@ var DocumentParser = class {
|
|
|
8503
9130
|
return `[PDF Parsing Error for ${fileName}]`;
|
|
8504
9131
|
}
|
|
8505
9132
|
}
|
|
8506
|
-
if (extension === "docx" || mimeType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document") {
|
|
9133
|
+
if (extension === "docx" || extension === "doc" || mimeType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document" || mimeType === "application/msword") {
|
|
8507
9134
|
try {
|
|
8508
9135
|
const mammoth = await import("mammoth");
|
|
8509
9136
|
const buffer = Buffer.isBuffer(file) ? file : Buffer.from(await file.arrayBuffer());
|
|
8510
9137
|
const result = await mammoth.extractRawText({ buffer });
|
|
8511
9138
|
return result.value;
|
|
8512
9139
|
} catch (err) {
|
|
8513
|
-
console.warn('[DocumentParser]
|
|
8514
|
-
return `[
|
|
9140
|
+
console.warn('[DocumentParser] Word parsing failed. Make sure "mammoth" is installed.', err);
|
|
9141
|
+
return `[Word Parsing Error for ${fileName}]`;
|
|
8515
9142
|
}
|
|
8516
9143
|
}
|
|
8517
9144
|
try {
|
|
@@ -8567,7 +9194,7 @@ var DatabaseStorage = class {
|
|
|
8567
9194
|
try {
|
|
8568
9195
|
const payload = LicenseVerifier.verify(this.config.licenseKey, this.config.projectId);
|
|
8569
9196
|
const tier = (payload.tier || "").toLowerCase();
|
|
8570
|
-
if (tier !== "premium" && tier !== "enterprise" && tier !== "growth" && tier !== "pro" && tier !== "developer_pro") {
|
|
9197
|
+
if (tier !== "premium" && tier !== "enterprise" && tier !== "growth" && tier !== "pro" && tier !== "developer_pro" && tier !== "free_trial" && tier !== "free_tier" && tier !== "free" && tier !== "hobby") {
|
|
8571
9198
|
throw new Error(
|
|
8572
9199
|
`[Retrivora SDK] Chat History and Feedback features are not available on the "${tier}" tier. Please upgrade to a Developer Pro or Enterprise plan.`
|
|
8573
9200
|
);
|
|
@@ -9335,6 +9962,7 @@ function createUploadHandler(configOrPlugin, options) {
|
|
|
9335
9962
|
}
|
|
9336
9963
|
const documents = [];
|
|
9337
9964
|
for (const file of files) {
|
|
9965
|
+
const isExcel = file.name.toLowerCase().endsWith(".xlsx") || file.name.toLowerCase().endsWith(".xls") || file.type.includes("spreadsheet") || file.type.includes("excel");
|
|
9338
9966
|
if (file.name.toLowerCase().endsWith(".csv") || file.type === "text/csv") {
|
|
9339
9967
|
const text = await file.text();
|
|
9340
9968
|
const Papa = await import("papaparse");
|
|
@@ -9367,7 +9995,7 @@ function createUploadHandler(configOrPlugin, options) {
|
|
|
9367
9995
|
metadata: __spreadValues(__spreadProps(__spreadValues({
|
|
9368
9996
|
fileName: file.name,
|
|
9369
9997
|
fileSize: file.size,
|
|
9370
|
-
fileType: file.type,
|
|
9998
|
+
fileType: file.type || "text/csv",
|
|
9371
9999
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9372
10000
|
}, dimension ? { dimension } : {}), {
|
|
9373
10001
|
csvHeaders: csvCols
|
|
@@ -9375,6 +10003,42 @@ function createUploadHandler(configOrPlugin, options) {
|
|
|
9375
10003
|
});
|
|
9376
10004
|
}
|
|
9377
10005
|
}
|
|
10006
|
+
} else if (isExcel) {
|
|
10007
|
+
const xlsx = await import("xlsx");
|
|
10008
|
+
const buffer = Buffer.isBuffer(file) ? file : Buffer.from(await file.arrayBuffer());
|
|
10009
|
+
const workbook = xlsx.read(buffer, { type: "buffer" });
|
|
10010
|
+
for (const sheetName of workbook.SheetNames) {
|
|
10011
|
+
const sheet = workbook.Sheets[sheetName];
|
|
10012
|
+
const jsonRows = xlsx.utils.sheet_to_json(sheet, { defval: "" });
|
|
10013
|
+
if (jsonRows && jsonRows.length > 0) {
|
|
10014
|
+
let i = 0;
|
|
10015
|
+
const headers = Object.keys(jsonRows[0] || {});
|
|
10016
|
+
for (const rowData of jsonRows) {
|
|
10017
|
+
i++;
|
|
10018
|
+
const contentParts = [];
|
|
10019
|
+
for (const [key, val] of Object.entries(rowData)) {
|
|
10020
|
+
if (key && val !== void 0 && val !== null && String(val).trim()) {
|
|
10021
|
+
contentParts.push(`${key}: ${val}`);
|
|
10022
|
+
}
|
|
10023
|
+
}
|
|
10024
|
+
if (contentParts.length > 0) {
|
|
10025
|
+
documents.push({
|
|
10026
|
+
docId: `${file.name}-${sheetName}-row-${i}`,
|
|
10027
|
+
content: contentParts.join(", "),
|
|
10028
|
+
metadata: __spreadValues(__spreadProps(__spreadValues({
|
|
10029
|
+
fileName: file.name,
|
|
10030
|
+
sheetName,
|
|
10031
|
+
fileSize: file.size,
|
|
10032
|
+
fileType: file.type || "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
10033
|
+
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10034
|
+
}, dimension ? { dimension } : {}), {
|
|
10035
|
+
headers
|
|
10036
|
+
}), rowData)
|
|
10037
|
+
});
|
|
10038
|
+
}
|
|
10039
|
+
}
|
|
10040
|
+
}
|
|
10041
|
+
}
|
|
9378
10042
|
} else {
|
|
9379
10043
|
const content = await DocumentParser.parse(file, file.name, file.type);
|
|
9380
10044
|
documents.push({
|