@retrivora-ai/rag-engine 1.7.9 → 1.8.0

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.
@@ -123,19 +123,19 @@ Classify the query into ONE of the following intents:
123
123
 
124
124
  ---
125
125
 
126
- ## 2. VIEW MAPPING (STRICT)
126
+ ## 2. VIEW SELECTION (DYNAMIC)
127
127
 
128
- | Intent | View |
129
- |----------|-----------|
130
- | explore | carousel |
131
- | analyze | chart |
132
- | compare | table |
133
- | detail | table |
128
+ Choose the best primary "view" ("carousel", "chart", or "table") based on the data:
129
+ - TRENDS/GROWTH → "chart" (type: "line")
130
+ - DISTRIBUTIONS/RATIOS → "chart" (type: "pie")
131
+ - COMPARISONS → "chart" (type: "bar")
132
+ - PRODUCT LISTS → "carousel"
133
+ - RAW DATA/SPECS → "table"
134
134
 
135
- ⚠️ Override Rules:
136
- - If query contains "distribution", "percentage", "ratio" ALWAYS chart
137
- - If query contains "show products", "list items" ALWAYS carousel
138
- - NEVER mix views
135
+ ⚠️ Dynamic Overrides:
136
+ - If query is "show products", ALWAYS use carousel.
137
+ - If data is statistical, ALWAYS use a chart.
138
+ - NEVER explain why you chose a view.
139
139
 
140
140
  ---
141
141
 
@@ -193,7 +193,8 @@ Classify the query into ONE of the following intents:
193
193
  {
194
194
  "label": "string",
195
195
  "value": number,
196
- "inStock": boolean (optional)
196
+ "inStockCount": number (optional),
197
+ "outOfStockCount": number (optional)
197
198
  }
198
199
 
199
200
  ### 🔹 TABLE FORMAT
@@ -204,14 +205,14 @@ Classify the query into ONE of the following intents:
204
205
 
205
206
  ## 5. DATA RULES
206
207
 
207
- - NEVER hallucinate fields or data
208
- - ONLY use retrieved vector DB data
209
- - NO placeholders ("...", "N/A")
210
- - If image/link missing REMOVE FIELD
211
- - Aggregate BEFORE rendering chart
212
- - IF NO DATA IS FOUND in the retrieved context:
213
- DO NOT output a \`\`\`ui\`\`\` block
214
- Instead, state clearly that no relevant products or data were found.
208
+ - NEVER hallucinate fields or data.
209
+ - ONLY use data provided in the retrieved context.
210
+ - STRICT DATA ISOLATION: The labels and numbers in the EXAMPLES section below are placeholders. NEVER use them.
211
+ - IF NO DATA IS FOUND: You MUST NOT generate a chart. You MUST NOT say "This example assumes" or provide hypothetical numbers. Simply state "No relevant data found in the database." and STOP.
212
+ - NO META-COMMENTARY: NEVER talk about the task itself (e.g., "I've used JSON", "In a real-world app", "Dedicated library").
213
+ - NEVER explain YOUR formatting choice.
214
+ - Aggregate BEFORE rendering chart.
215
+ - DYNAMIC INTELLIGENCE: Autonomously extract relevant categories/values from the context and calculate totals for the "data" array based on the user query.
215
216
 
216
217
  ---
217
218
 
@@ -228,11 +229,12 @@ Classify the query into ONE of the following intents:
228
229
 
229
230
  ## 7. OUTPUT RULES
230
231
 
231
- - ALWAYS return EXACTLY ONE \`\`\`ui\`\`\` block
232
- - JSON must be VALID
233
- - No explanation inside block
234
- - NEVER use ASCII charts, Markdown tables, or text-based drawings for data.
235
- - If data is tabular or statistical, ALWAYS use the \`\`\`ui\`\`\` block.
232
+ - ALWAYS return EXACTLY ONE \`\`\`ui\`\`\` block.
233
+ - JSON must be VALID.
234
+ - FIELD MAPPING (MANDATORY): Always use "label" for the name/category and "value" for the numeric count.
235
+ - NO xKey or yKeys: Do not include these fields; the UI uses "label" and "value" by default.
236
+ - SILENT DATA: If a chart is provided, DO NOT say "Here is the JSON" or "Below is the data". Simply output the block.
237
+ - TEXT OUTSIDE THE BLOCK: One brief sentence maximum.
236
238
 
237
239
  ---
238
240
 
@@ -243,8 +245,8 @@ User: "show me beauty products"
243
245
  \`\`\`ui
244
246
  {
245
247
  "view": "carousel",
246
- "title": "Beauty Products",
247
- "description": "Browse available beauty items",
248
+ "title": "TITLE",
249
+ "description": "DESCRIPTION",
248
250
  "metadata": {
249
251
  "intent": "explore",
250
252
  "confidence": 0.95
@@ -252,43 +254,41 @@ User: "show me beauty products"
252
254
  "carousel": {
253
255
  "items": [
254
256
  {
255
- "id": "1",
256
- "name": "Mascara",
257
- "brand": "Essence",
258
- "price": 9.99,
259
- "image": "https://example.com/mascara.jpg",
260
- "link": "https://example.com/p1",
257
+ "id": "ID",
258
+ "name": "PRODUCT_NAME",
259
+ "brand": "BRAND",
260
+ "price": 0,
261
+ "image": "URL",
262
+ "link": "URL",
261
263
  "inStock": true
262
264
  }
263
265
  ]
264
266
  },
265
- "insights": ["Most products are affordable"]
267
+ "insights": ["INSIGHT"]
266
268
  }
267
269
  \`\`\`
268
270
 
269
271
  ---
270
272
 
271
- User: "distribution of products across categories"
273
+
272
274
 
273
275
  \`\`\`ui
274
276
  {
275
277
  "view": "chart",
276
- "title": "Product Distribution",
277
- "description": "Category-wise product distribution",
278
+ "title": "TITLE",
279
+ "description": "DESCRIPTION",
278
280
  "metadata": {
279
281
  "intent": "analyze",
280
282
  "confidence": 0.98
281
283
  },
282
284
  "chart": {
283
285
  "type": "pie",
284
- "xKey": "label",
285
- "yKeys": ["value"],
286
286
  "data": [
287
- { "label": "Beauty", "value": 15 },
288
- { "label": "Electronics", "value": 10 }
287
+ { "label": "LABEL_A", "value": 0, "inStockCount": 0, "outOfStockCount": 0 },
288
+ { "label": "LABEL_B", "value": 0, "inStockCount": 0, "outOfStockCount": 0 }
289
289
  ]
290
290
  },
291
- "insights": ["Beauty dominates inventory"]
291
+ "insights": ["INSIGHT"]
292
292
  }
293
293
  \`\`\`
294
294
  `;
@@ -487,16 +487,18 @@ User: "distribution of products across categories"
487
487
  let sources: VectorMatch[] = [];
488
488
  let graphData: GraphSearchResult | undefined;
489
489
 
490
+ let uiTransformation: any;
490
491
  for await (const chunk of stream) {
491
492
  if (typeof chunk === 'string') {
492
493
  reply += chunk;
493
- } else if ('sources' in chunk) {
494
- sources = chunk.sources;
495
- graphData = chunk.graphData;
494
+ } else if (typeof chunk === 'object' && chunk !== null) {
495
+ if ('sources' in chunk) sources = chunk.sources;
496
+ if ('graphData' in chunk) graphData = chunk.graphData;
497
+ if ('ui_transformation' in chunk) uiTransformation = chunk.ui_transformation;
496
498
  }
497
499
  }
498
500
 
499
- return { reply, sources, graphData };
501
+ return { reply, sources, graphData, ui_transformation: uiTransformation };
500
502
  }
501
503
 
502
504
  /**
@@ -22,6 +22,7 @@ export interface ChatResponse {
22
22
  reply: string;
23
23
  sources: VectorMatch[];
24
24
  graphData?: GraphSearchResult;
25
+ ui_transformation?: unknown;
25
26
  }
26
27
 
27
28
  export interface GraphNode {
@@ -27,7 +27,8 @@ export interface UITransformationResponse {
27
27
  export interface PieChartData {
28
28
  label: string;
29
29
  value: number;
30
- inStock?: boolean;
30
+ inStockCount?: number;
31
+ outOfStockCount?: number;
31
32
  [key: string]: unknown;
32
33
  }
33
34
 
@@ -37,7 +38,8 @@ export interface PieChartData {
37
38
  export interface BarChartData {
38
39
  category: string;
39
40
  value: number;
40
- inStock?: boolean;
41
+ inStockCount?: number;
42
+ outOfStockCount?: number;
41
43
  [key: string]: unknown;
42
44
  }
43
45
 
@@ -202,11 +204,15 @@ export class UITransformer {
202
204
  const categories = this.detectCategories(data);
203
205
  const categoryData = this.aggregateByCategory(data, categories);
204
206
 
205
- const pieData: PieChartData[] = Object.entries(categoryData).map(([label, count]) => ({
206
- label,
207
- value: count as number,
208
- inStock: this.checkStockStatus(label, data),
209
- }));
207
+ const pieData: PieChartData[] = Object.entries(categoryData).map(([label, count]) => {
208
+ const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data);
209
+ return {
210
+ label,
211
+ value: count as number,
212
+ inStockCount,
213
+ outOfStockCount,
214
+ };
215
+ });
210
216
 
211
217
  return {
212
218
  type: 'pie_chart',
@@ -225,11 +231,15 @@ export class UITransformer {
225
231
  const categories = this.detectCategories(data);
226
232
  const categoryData = this.aggregateByCategory(data, categories);
227
233
 
228
- const barData: BarChartData[] = Object.entries(categoryData).map(([category, value]) => ({
229
- category,
230
- value: value as number,
231
- inStock: this.checkStockStatus(category, data),
232
- }));
234
+ const barData: BarChartData[] = Object.entries(categoryData).map(([category, value]) => {
235
+ const { inStockCount, outOfStockCount } = this.calculateStockCounts(category, data);
236
+ return {
237
+ category,
238
+ value: value as number,
239
+ inStockCount,
240
+ outOfStockCount,
241
+ };
242
+ });
233
243
 
234
244
  return {
235
245
  type: 'bar_chart',
@@ -477,15 +487,25 @@ export class UITransformer {
477
487
  }
478
488
 
479
489
  /**
480
- * Helper: Check stock status
490
+ * Helper: Calculate stock counts by category
481
491
  */
482
- private static checkStockStatus(category: string, data: VectorMatch[]): boolean {
483
- const item = data.find(d => {
492
+ private static calculateStockCounts(category: string, data: VectorMatch[]): { inStockCount: number; outOfStockCount: number } {
493
+ let inStock = 0;
494
+ let outOfStock = 0;
495
+
496
+ data.forEach(d => {
484
497
  const meta = d.metadata || {};
485
- return meta.category === category || meta.type === category;
498
+ const itemCategory = (meta.category || meta.type || 'Other') as string;
499
+ if (itemCategory === category) {
500
+ if (this.determineStockStatus(d)) {
501
+ inStock++;
502
+ } else {
503
+ outOfStock++;
504
+ }
505
+ }
486
506
  });
487
507
 
488
- return this.determineStockStatus(item || ({} as VectorMatch));
508
+ return { inStockCount: inStock, outOfStockCount: outOfStock };
489
509
  }
490
510
 
491
511
  /**