@retrivora-ai/rag-engine 1.8.2 → 1.8.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.
Files changed (49) hide show
  1. package/dist/handlers/index.js +142 -16
  2. package/dist/handlers/index.mjs +5848 -17
  3. package/dist/index.css +3162 -0
  4. package/dist/index.d.mts +9 -15
  5. package/dist/index.d.ts +9 -15
  6. package/dist/index.js +1355 -919
  7. package/dist/index.mjs +1437 -896
  8. package/dist/server.d.mts +6 -0
  9. package/dist/server.d.ts +6 -0
  10. package/dist/server.js +171 -17
  11. package/dist/server.mjs +5923 -68
  12. package/package.json +8 -6
  13. package/src/app/globals.css +35 -11
  14. package/src/components/ChatWidget.tsx +0 -1
  15. package/src/components/MarkdownComponents.tsx +3 -0
  16. package/src/components/MessageBubble.tsx +3 -2
  17. package/src/components/ObservabilityPanel.tsx +1 -1
  18. package/src/components/UIDispatcher.tsx +0 -3
  19. package/src/config/ConfigBuilder.ts +38 -1
  20. package/src/core/LangChainAgent.ts +1 -4
  21. package/src/core/Pipeline.ts +31 -18
  22. package/src/core/QueryProcessor.ts +65 -0
  23. package/src/rag/Reranker.ts +99 -6
  24. package/src/tailwind.css +2 -0
  25. package/src/utils/ProductExtractor.ts +3 -3
  26. package/dist/ChromaDBProvider-MIDOR4FW.mjs +0 -8
  27. package/dist/MilvusProvider-U7SKC27V.mjs +0 -8
  28. package/dist/MongoDBProvider-YNKC7EJ6.mjs +0 -8
  29. package/dist/MultiTablePostgresProvider-ZLGSKTJR.mjs +0 -8
  30. package/dist/PineconeProvider-QZNRKTN2.mjs +0 -8
  31. package/dist/QdrantProvider-RLJTNGPY.mjs +0 -8
  32. package/dist/RedisProvider-SR65SCKV.mjs +0 -8
  33. package/dist/SimpleGraphProvider-SLOXO4M7.mjs +0 -62
  34. package/dist/UniversalVectorProvider-IN67OS56.mjs +0 -9
  35. package/dist/WeaviateProvider-5FWDFITI.mjs +0 -8
  36. package/dist/chunk-5AJ4XHLW.mjs +0 -201
  37. package/dist/chunk-5YGUXK7Z.mjs +0 -80
  38. package/dist/chunk-CFVEZTBJ.mjs +0 -102
  39. package/dist/chunk-ICKRMZQK.mjs +0 -76
  40. package/dist/chunk-IMP6FUCY.mjs +0 -30
  41. package/dist/chunk-LR3VMDVK.mjs +0 -157
  42. package/dist/chunk-LZVVLSDN.mjs +0 -4077
  43. package/dist/chunk-M6JSPGAR.mjs +0 -117
  44. package/dist/chunk-OZFBG4BA.mjs +0 -291
  45. package/dist/chunk-PSFPZXHX.mjs +0 -245
  46. package/dist/chunk-U55XRW3U.mjs +0 -96
  47. package/dist/chunk-VUQJVIJT.mjs +0 -148
  48. package/dist/chunk-X4TOT24V.mjs +0 -89
  49. package/dist/chunk-YLTMFW4M.mjs +0 -49
package/dist/server.d.mts CHANGED
@@ -353,6 +353,7 @@ declare class ConfigBuilder {
353
353
  private _embedding?;
354
354
  private _ui?;
355
355
  private _rag?;
356
+ private _graphDb?;
356
357
  /**
357
358
  * Set the project/application ID for namespacing
358
359
  */
@@ -369,6 +370,10 @@ declare class ConfigBuilder {
369
370
  * Configure the embedding provider
370
371
  */
371
372
  embedding(provider: EmbeddingProvider | 'auto', model?: string, apiKey?: string, options?: Record<string, unknown>): this;
373
+ /**
374
+ * Configure the graph database provider
375
+ */
376
+ graphDb(provider: string | 'auto', options?: Record<string, unknown>): this;
372
377
  /**
373
378
  * Set RAG-specific pipeline parameters
374
379
  */
@@ -390,6 +395,7 @@ declare class ConfigBuilder {
390
395
  private _autoDetectVectorDb;
391
396
  private _autoDetectLLM;
392
397
  private _autoDetectEmbedding;
398
+ private _autoDetectGraphDb;
393
399
  }
394
400
  /**
395
401
  * Preset configurations for common provider combinations
package/dist/server.d.ts CHANGED
@@ -353,6 +353,7 @@ declare class ConfigBuilder {
353
353
  private _embedding?;
354
354
  private _ui?;
355
355
  private _rag?;
356
+ private _graphDb?;
356
357
  /**
357
358
  * Set the project/application ID for namespacing
358
359
  */
@@ -369,6 +370,10 @@ declare class ConfigBuilder {
369
370
  * Configure the embedding provider
370
371
  */
371
372
  embedding(provider: EmbeddingProvider | 'auto', model?: string, apiKey?: string, options?: Record<string, unknown>): this;
373
+ /**
374
+ * Configure the graph database provider
375
+ */
376
+ graphDb(provider: string | 'auto', options?: Record<string, unknown>): this;
372
377
  /**
373
378
  * Set RAG-specific pipeline parameters
374
379
  */
@@ -390,6 +395,7 @@ declare class ConfigBuilder {
390
395
  private _autoDetectVectorDb;
391
396
  private _autoDetectLLM;
392
397
  private _autoDetectEmbedding;
398
+ private _autoDetectGraphDb;
393
399
  }
394
400
  /**
395
401
  * Preset configurations for common provider combinations
package/dist/server.js CHANGED
@@ -3451,11 +3451,70 @@ Text to extract from:
3451
3451
  // src/rag/Reranker.ts
3452
3452
  var Reranker = class {
3453
3453
  /**
3454
- * Re-ranks matches based on a secondary relevance score.
3455
- * In a production environment, this would call a Cross-Encoder model.
3456
- * Here we implement a placeholder that filters by score and limits count.
3454
+ * Re-ranks matches based on a secondary relevance score using an LLM.
3457
3455
  */
3458
- async rerank(matches, query, limit = 5) {
3456
+ async rerank(matches, query, limit = 5, llm) {
3457
+ if (!llm || matches.length <= 1) {
3458
+ const keywords = query.toLowerCase().replace(/[^\w\s]/g, "").split(/\s+/).filter((w) => w.length > 2);
3459
+ if (keywords.length === 0) {
3460
+ return matches.sort((a, b) => b.score - a.score).slice(0, limit);
3461
+ }
3462
+ const scoredMatches = matches.map((match) => {
3463
+ const contentLower = match.content.toLowerCase();
3464
+ let keywordScore = 0;
3465
+ keywords.forEach((keyword) => {
3466
+ if (contentLower.includes(keyword)) {
3467
+ keywordScore += 1;
3468
+ }
3469
+ });
3470
+ const normalizedKeywordScore = keywordScore / keywords.length;
3471
+ const combinedScore = match.score + normalizedKeywordScore * 0.5;
3472
+ return __spreadProps(__spreadValues({}, match), {
3473
+ score: combinedScore
3474
+ });
3475
+ });
3476
+ return scoredMatches.sort((a, b) => b.score - a.score).slice(0, limit);
3477
+ }
3478
+ try {
3479
+ const topN = matches.slice(0, 10);
3480
+ const prompt = `You are a relevance ranking expert.
3481
+ Given the following user query and a list of retrieved document chunks, rank the chunks by relevance to the query.
3482
+ Return the result as a comma-separated list of document indices in order of relevance, from most relevant to least relevant.
3483
+ Use the indices provided in brackets like [0], [1], etc.
3484
+ Only return the indices (e.g., "2,0,1"), nothing else. Do not include brackets in the output.
3485
+
3486
+ Query: "${query}"
3487
+
3488
+ Documents:
3489
+ ${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}`;
3490
+ const response = await llm.chat(
3491
+ [{ role: "user", content: prompt }],
3492
+ "",
3493
+ { temperature: 0, maxTokens: 50 }
3494
+ );
3495
+ const cleanedResponse = response.trim().replace(/[\[\]\s]/g, "");
3496
+ const rankedIndices = cleanedResponse.split(",").map(Number).filter((n) => !isNaN(n));
3497
+ if (rankedIndices.length > 0) {
3498
+ const rankedMatches = [];
3499
+ const usedIndices = /* @__PURE__ */ new Set();
3500
+ for (const index of rankedIndices) {
3501
+ if (index >= 0 && index < topN.length && !usedIndices.has(index)) {
3502
+ rankedMatches.push(topN[index]);
3503
+ usedIndices.add(index);
3504
+ }
3505
+ }
3506
+ for (let i = 0; i < topN.length; i++) {
3507
+ if (!usedIndices.has(i)) {
3508
+ rankedMatches.push(topN[i]);
3509
+ }
3510
+ }
3511
+ const rest = matches.slice(10);
3512
+ rankedMatches.push(...rest);
3513
+ return rankedMatches.slice(0, limit);
3514
+ }
3515
+ } catch (error) {
3516
+ console.warn("[Reranker] LLM re-ranking failed, falling back to score-based sorting:", error);
3517
+ }
3459
3518
  return matches.sort((a, b) => b.score - a.score).slice(0, limit);
3460
3519
  }
3461
3520
  };
@@ -3515,7 +3574,7 @@ var LangChainAgent = class {
3515
3574
  */
3516
3575
  async initialize(chatModel) {
3517
3576
  try {
3518
- const [{ DynamicTool }, { HumanMessage }, { createAgent }] = await Promise.all([
3577
+ const [{ DynamicTool }, , { createAgent }] = await Promise.all([
3519
3578
  import("@langchain/core/tools"),
3520
3579
  import("@langchain/core/messages"),
3521
3580
  import("langchain")
@@ -3565,7 +3624,6 @@ When presenting structured data, statistics, or comparisons, decide if it is bes
3565
3624
  tools: [searchTool],
3566
3625
  systemPrompt: finalSystemPrompt
3567
3626
  });
3568
- void HumanMessage;
3569
3627
  } catch (error) {
3570
3628
  const isMissing = error instanceof Error && error.message.includes("Cannot find module");
3571
3629
  const hint = isMissing ? " Make sure 'langchain' and '@langchain/core' are installed:\n npm install langchain @langchain/core" : "";
@@ -4049,6 +4107,69 @@ var QueryProcessor = class {
4049
4107
  if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
4050
4108
  return filter;
4051
4109
  }
4110
+ /**
4111
+ * Determines the retrieval strategy based on the query content.
4112
+ */
4113
+ static async determineRetrievalStrategy(query, llm, customGraphKeywords, customVectorKeywords) {
4114
+ if (llm) {
4115
+ try {
4116
+ const prompt = `You are a routing expert in a RAG system.
4117
+ Given the user query, determine which retrieval mechanism is needed:
4118
+ - 'vector': For semantic search, finding specific documents or content based on similarity.
4119
+ - 'graph': For finding relationships between entities, multi-hop questions, or network structures.
4120
+ - 'both': If the query requires both specific content and relationship/structural analysis.
4121
+
4122
+ Query: "${query}"
4123
+
4124
+ Return ONLY 'vector', 'graph', or 'both'. No explanation.`;
4125
+ const response = await llm.chat(
4126
+ [{ role: "user", content: prompt }],
4127
+ "",
4128
+ { temperature: 0, maxTokens: 10 }
4129
+ );
4130
+ const cleanResponse = response.trim().toLowerCase();
4131
+ if (cleanResponse.includes("vector")) return "vector";
4132
+ if (cleanResponse.includes("graph")) return "graph";
4133
+ if (cleanResponse.includes("both")) return "both";
4134
+ } catch (error) {
4135
+ console.warn("[QueryProcessor] LLM strategy classification failed, falling back to keywords:", error);
4136
+ }
4137
+ }
4138
+ const normalized = query.toLowerCase();
4139
+ const graphKeywords = customGraphKeywords || [
4140
+ "relationship",
4141
+ "connect",
4142
+ "link",
4143
+ "network",
4144
+ "friend",
4145
+ "colleague",
4146
+ "manager",
4147
+ "hierarchy",
4148
+ "multi-hop",
4149
+ "shortest path",
4150
+ "between",
4151
+ "related to",
4152
+ "associated with",
4153
+ "belongs to",
4154
+ "part of"
4155
+ ];
4156
+ const hasGraphKeyword = graphKeywords.some((kw) => normalized.includes(kw));
4157
+ const vectorKeywords = customVectorKeywords || [
4158
+ "find",
4159
+ "search",
4160
+ "tell me about",
4161
+ "what is",
4162
+ "how to",
4163
+ "documents about"
4164
+ ];
4165
+ const hasVectorKeyword = vectorKeywords.some((kw) => normalized.includes(kw));
4166
+ if (hasGraphKeyword && !hasVectorKeyword) {
4167
+ return "graph";
4168
+ } else if (hasGraphKeyword && hasVectorKeyword) {
4169
+ return "both";
4170
+ }
4171
+ return "vector";
4172
+ }
4052
4173
  };
4053
4174
 
4054
4175
  // src/utils/synonyms.ts
@@ -5158,6 +5279,7 @@ var Pipeline = class {
5158
5279
  let context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
5159
5280
  ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
5160
5281
  if (graphData && graphData.nodes.length > 0) {
5282
+ console.log(`[Graph Retrieval] Found ${graphData.nodes.length} relevant entities.`);
5161
5283
  const graphContext = graphData.nodes.map(
5162
5284
  (n) => `Entity: ${n.label} (${n.id})${n.properties ? " - " + JSON.stringify(n.properties) : ""}`
5163
5285
  ).join("\n");
@@ -5223,7 +5345,7 @@ ${context}`;
5223
5345
  };
5224
5346
  const hallucinationResult = yield new __await(scoreHallucination(this.llmProvider, fullReply, context).catch(() => void 0));
5225
5347
  const trainedSchema = yield new __await(trainingPromise);
5226
- const uiTransformation = yield new __await(this.generateUiTransformation(question, sources, context, trainedSchema));
5348
+ const uiTransformation = yield new __await(this.generateUiTransformation(question, sources, trainedSchema));
5227
5349
  const trace = {
5228
5350
  requestId,
5229
5351
  query: question,
@@ -5262,7 +5384,7 @@ ${context}`;
5262
5384
  * Universal retrieval method combining all enabled providers.
5263
5385
  * Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
5264
5386
  */
5265
- async generateUiTransformation(question, sources, _context, trainedSchema) {
5387
+ async generateUiTransformation(question, sources, trainedSchema) {
5266
5388
  if (!sources || sources.length === 0) {
5267
5389
  return UITransformer.transform(question, sources, this.config, trainedSchema);
5268
5390
  }
@@ -5279,15 +5401,19 @@ ${context}`;
5279
5401
  const topK = (_b = options.topK) != null ? _b : 5;
5280
5402
  const cacheKey = `${ns}::${query}`;
5281
5403
  let queryVector = this.embeddingCache.get(cacheKey);
5404
+ const strategy = await QueryProcessor.determineRetrievalStrategy(query, this.llmProvider);
5405
+ console.debug(`[Pipeline] Determined retrieval strategy: ${strategy}`);
5282
5406
  const [retrievedVector, graphData] = await Promise.all([
5283
- queryVector ? Promise.resolve(queryVector) : this.embeddingProvider.embed(query, { taskType: "query" }),
5284
- this.graphDB && ((_c = this.config.rag) == null ? void 0 : _c.useGraphRetrieval) ? this.graphDB.query(query) : Promise.resolve(void 0)
5407
+ // Only embed if we need vector search (strategy is 'vector' or 'both')
5408
+ strategy === "vector" || strategy === "both" ? queryVector ? Promise.resolve(queryVector) : this.embeddingProvider.embed(query, { taskType: "query" }) : Promise.resolve([]),
5409
+ // Only query graph if we need graph search (strategy is 'graph' or 'both')
5410
+ (strategy === "graph" || strategy === "both") && this.graphDB && ((_c = this.config.rag) == null ? void 0 : _c.useGraphRetrieval) ? this.graphDB.query(query) : Promise.resolve(void 0)
5285
5411
  ]);
5286
- if (!queryVector) {
5412
+ if ((strategy === "vector" || strategy === "both") && !queryVector && retrievedVector && retrievedVector.length > 0) {
5287
5413
  this.embeddingCache.set(cacheKey, retrievedVector);
5288
5414
  queryVector = retrievedVector;
5289
5415
  }
5290
- const sources = await this.vectorDB.query(queryVector, topK, ns, options.filter);
5416
+ const sources = (strategy === "vector" || strategy === "both") && queryVector && queryVector.length > 0 ? await this.vectorDB.query(queryVector, topK, ns, options.filter) : [];
5291
5417
  return { sources, graphData };
5292
5418
  }
5293
5419
  /** Rewrite the user query for better retrieval performance. */
@@ -5316,13 +5442,13 @@ Optimized Search Query:`;
5316
5442
  await this.initialize();
5317
5443
  const ns = namespace != null ? namespace : this.config.projectId;
5318
5444
  try {
5319
- const { sources } = await this.retrieve(query, { namespace: ns, topK: 3 });
5445
+ const { sources } = await this.retrieve(query, { namespace: ns, topK: 5 });
5320
5446
  if (sources.length === 0) return [];
5321
5447
  const context = sources.map((s) => s.content).join("\n\n---\n\n");
5322
- const prompt = `Based on the following snippets from a document, what are 3 short, relevant questions a user might ask?
5448
+ const prompt = `Based on the following snippets from a document, what are at least 5 short, relevant questions a user might ask?
5323
5449
  Focus on questions that can be answered by the context.
5324
5450
  Keep each question under 10 words and make them very specific to the content.
5325
- Return ONLY a JSON array of strings like ["Question 1", "Question 2", "Question 3"].
5451
+ Return ONLY a JSON array of strings like ["Question 1", "Question 2", "Question 3", "Question 4", "Question 5"].
5326
5452
 
5327
5453
  Context:
5328
5454
  ${context}
@@ -5339,7 +5465,7 @@ Suggestions:`;
5339
5465
  if (match) {
5340
5466
  const suggestions = JSON.parse(match[0]);
5341
5467
  if (Array.isArray(suggestions)) {
5342
- return suggestions.map((s) => String(s)).slice(0, 3);
5468
+ return suggestions.map((s) => String(s)).slice(0, 5);
5343
5469
  }
5344
5470
  }
5345
5471
  } catch (error) {
@@ -5581,6 +5707,20 @@ var ConfigBuilder = class {
5581
5707
  }
5582
5708
  return this;
5583
5709
  }
5710
+ /**
5711
+ * Configure the graph database provider
5712
+ */
5713
+ graphDb(provider, options) {
5714
+ if (provider === "auto") {
5715
+ this._graphDb = this._autoDetectGraphDb();
5716
+ } else {
5717
+ this._graphDb = {
5718
+ provider,
5719
+ options: options != null ? options : {}
5720
+ };
5721
+ }
5722
+ return this;
5723
+ }
5584
5724
  /**
5585
5725
  * Set RAG-specific pipeline parameters
5586
5726
  */
@@ -5614,13 +5754,18 @@ var ConfigBuilder = class {
5614
5754
  ${missing.join("\n ")}`
5615
5755
  );
5616
5756
  }
5757
+ const ragConfig = (_a = this._rag) != null ? _a : { chunkSize: 1e3, chunkOverlap: 200, topK: 5 };
5758
+ if (process.env.RAG_USE_GRAPH_RETRIEVAL === "true") {
5759
+ ragConfig.useGraphRetrieval = true;
5760
+ }
5617
5761
  return __spreadValues({
5618
5762
  projectId: this._projectId,
5619
5763
  vectorDb: this._vectorDb,
5620
5764
  llm: this._llm,
5621
5765
  embedding: this._embedding,
5622
5766
  ui: this._rag ? this._ui : void 0,
5623
- rag: (_a = this._rag) != null ? _a : { chunkSize: 1e3, chunkOverlap: 200, topK: 5 }
5767
+ rag: ragConfig,
5768
+ graphDb: this._graphDb
5624
5769
  }, this._ui ? { ui: this._ui } : {});
5625
5770
  }
5626
5771
  /**
@@ -5668,6 +5813,15 @@ var ConfigBuilder = class {
5668
5813
  }
5669
5814
  return { provider: "openai", model: "text-embedding-3-small", apiKey: process.env.OPENAI_API_KEY };
5670
5815
  }
5816
+ _autoDetectGraphDb() {
5817
+ if (process.env.GRAPH_DB_PROVIDER) {
5818
+ return {
5819
+ provider: process.env.GRAPH_DB_PROVIDER,
5820
+ options: {}
5821
+ };
5822
+ }
5823
+ return void 0;
5824
+ }
5671
5825
  };
5672
5826
  var PRESETS = {
5673
5827
  /** OpenAI + Pinecone: Production-ready cloud setup */