@retrivora-ai/rag-engine 1.8.2 → 1.8.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.
Files changed (47) hide show
  1. package/dist/handlers/index.js +142 -16
  2. package/dist/handlers/index.mjs +5848 -17
  3. package/dist/index.d.mts +9 -15
  4. package/dist/index.d.ts +9 -15
  5. package/dist/index.js +1355 -919
  6. package/dist/index.mjs +1437 -896
  7. package/dist/server.d.mts +6 -0
  8. package/dist/server.d.ts +6 -0
  9. package/dist/server.js +171 -17
  10. package/dist/server.mjs +5923 -68
  11. package/package.json +6 -6
  12. package/src/app/globals.css +35 -11
  13. package/src/components/ChatWidget.tsx +0 -1
  14. package/src/components/MarkdownComponents.tsx +3 -0
  15. package/src/components/MessageBubble.tsx +3 -2
  16. package/src/components/ObservabilityPanel.tsx +1 -1
  17. package/src/components/UIDispatcher.tsx +0 -3
  18. package/src/config/ConfigBuilder.ts +38 -1
  19. package/src/core/LangChainAgent.ts +1 -4
  20. package/src/core/Pipeline.ts +31 -18
  21. package/src/core/QueryProcessor.ts +65 -0
  22. package/src/rag/Reranker.ts +99 -6
  23. package/src/utils/ProductExtractor.ts +3 -3
  24. package/dist/ChromaDBProvider-MIDOR4FW.mjs +0 -8
  25. package/dist/MilvusProvider-U7SKC27V.mjs +0 -8
  26. package/dist/MongoDBProvider-YNKC7EJ6.mjs +0 -8
  27. package/dist/MultiTablePostgresProvider-ZLGSKTJR.mjs +0 -8
  28. package/dist/PineconeProvider-QZNRKTN2.mjs +0 -8
  29. package/dist/QdrantProvider-RLJTNGPY.mjs +0 -8
  30. package/dist/RedisProvider-SR65SCKV.mjs +0 -8
  31. package/dist/SimpleGraphProvider-SLOXO4M7.mjs +0 -62
  32. package/dist/UniversalVectorProvider-IN67OS56.mjs +0 -9
  33. package/dist/WeaviateProvider-5FWDFITI.mjs +0 -8
  34. package/dist/chunk-5AJ4XHLW.mjs +0 -201
  35. package/dist/chunk-5YGUXK7Z.mjs +0 -80
  36. package/dist/chunk-CFVEZTBJ.mjs +0 -102
  37. package/dist/chunk-ICKRMZQK.mjs +0 -76
  38. package/dist/chunk-IMP6FUCY.mjs +0 -30
  39. package/dist/chunk-LR3VMDVK.mjs +0 -157
  40. package/dist/chunk-LZVVLSDN.mjs +0 -4077
  41. package/dist/chunk-M6JSPGAR.mjs +0 -117
  42. package/dist/chunk-OZFBG4BA.mjs +0 -291
  43. package/dist/chunk-PSFPZXHX.mjs +0 -245
  44. package/dist/chunk-U55XRW3U.mjs +0 -96
  45. package/dist/chunk-VUQJVIJT.mjs +0 -148
  46. package/dist/chunk-X4TOT24V.mjs +0 -89
  47. package/dist/chunk-YLTMFW4M.mjs +0 -49
@@ -3380,11 +3380,70 @@ Text to extract from:
3380
3380
  // src/rag/Reranker.ts
3381
3381
  var Reranker = class {
3382
3382
  /**
3383
- * Re-ranks matches based on a secondary relevance score.
3384
- * In a production environment, this would call a Cross-Encoder model.
3385
- * Here we implement a placeholder that filters by score and limits count.
3383
+ * Re-ranks matches based on a secondary relevance score using an LLM.
3386
3384
  */
3387
- async rerank(matches, query, limit = 5) {
3385
+ async rerank(matches, query, limit = 5, llm) {
3386
+ if (!llm || matches.length <= 1) {
3387
+ const keywords = query.toLowerCase().replace(/[^\w\s]/g, "").split(/\s+/).filter((w) => w.length > 2);
3388
+ if (keywords.length === 0) {
3389
+ return matches.sort((a, b) => b.score - a.score).slice(0, limit);
3390
+ }
3391
+ const scoredMatches = matches.map((match) => {
3392
+ const contentLower = match.content.toLowerCase();
3393
+ let keywordScore = 0;
3394
+ keywords.forEach((keyword) => {
3395
+ if (contentLower.includes(keyword)) {
3396
+ keywordScore += 1;
3397
+ }
3398
+ });
3399
+ const normalizedKeywordScore = keywordScore / keywords.length;
3400
+ const combinedScore = match.score + normalizedKeywordScore * 0.5;
3401
+ return __spreadProps(__spreadValues({}, match), {
3402
+ score: combinedScore
3403
+ });
3404
+ });
3405
+ return scoredMatches.sort((a, b) => b.score - a.score).slice(0, limit);
3406
+ }
3407
+ try {
3408
+ const topN = matches.slice(0, 10);
3409
+ const prompt = `You are a relevance ranking expert.
3410
+ Given the following user query and a list of retrieved document chunks, rank the chunks by relevance to the query.
3411
+ Return the result as a comma-separated list of document indices in order of relevance, from most relevant to least relevant.
3412
+ Use the indices provided in brackets like [0], [1], etc.
3413
+ Only return the indices (e.g., "2,0,1"), nothing else. Do not include brackets in the output.
3414
+
3415
+ Query: "${query}"
3416
+
3417
+ Documents:
3418
+ ${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}`;
3419
+ const response = await llm.chat(
3420
+ [{ role: "user", content: prompt }],
3421
+ "",
3422
+ { temperature: 0, maxTokens: 50 }
3423
+ );
3424
+ const cleanedResponse = response.trim().replace(/[\[\]\s]/g, "");
3425
+ const rankedIndices = cleanedResponse.split(",").map(Number).filter((n) => !isNaN(n));
3426
+ if (rankedIndices.length > 0) {
3427
+ const rankedMatches = [];
3428
+ const usedIndices = /* @__PURE__ */ new Set();
3429
+ for (const index of rankedIndices) {
3430
+ if (index >= 0 && index < topN.length && !usedIndices.has(index)) {
3431
+ rankedMatches.push(topN[index]);
3432
+ usedIndices.add(index);
3433
+ }
3434
+ }
3435
+ for (let i = 0; i < topN.length; i++) {
3436
+ if (!usedIndices.has(i)) {
3437
+ rankedMatches.push(topN[i]);
3438
+ }
3439
+ }
3440
+ const rest = matches.slice(10);
3441
+ rankedMatches.push(...rest);
3442
+ return rankedMatches.slice(0, limit);
3443
+ }
3444
+ } catch (error) {
3445
+ console.warn("[Reranker] LLM re-ranking failed, falling back to score-based sorting:", error);
3446
+ }
3388
3447
  return matches.sort((a, b) => b.score - a.score).slice(0, limit);
3389
3448
  }
3390
3449
  };
@@ -3444,7 +3503,7 @@ var LangChainAgent = class {
3444
3503
  */
3445
3504
  async initialize(chatModel) {
3446
3505
  try {
3447
- const [{ DynamicTool }, { HumanMessage }, { createAgent }] = await Promise.all([
3506
+ const [{ DynamicTool }, , { createAgent }] = await Promise.all([
3448
3507
  import("@langchain/core/tools"),
3449
3508
  import("@langchain/core/messages"),
3450
3509
  import("langchain")
@@ -3494,7 +3553,6 @@ When presenting structured data, statistics, or comparisons, decide if it is bes
3494
3553
  tools: [searchTool],
3495
3554
  systemPrompt: finalSystemPrompt
3496
3555
  });
3497
- void HumanMessage;
3498
3556
  } catch (error) {
3499
3557
  const isMissing = error instanceof Error && error.message.includes("Cannot find module");
3500
3558
  const hint = isMissing ? " Make sure 'langchain' and '@langchain/core' are installed:\n npm install langchain @langchain/core" : "";
@@ -3972,6 +4030,69 @@ var QueryProcessor = class {
3972
4030
  if (filter.keywords && filter.keywords.length === 0) delete filter.keywords;
3973
4031
  return filter;
3974
4032
  }
4033
+ /**
4034
+ * Determines the retrieval strategy based on the query content.
4035
+ */
4036
+ static async determineRetrievalStrategy(query, llm, customGraphKeywords, customVectorKeywords) {
4037
+ if (llm) {
4038
+ try {
4039
+ const prompt = `You are a routing expert in a RAG system.
4040
+ Given the user query, determine which retrieval mechanism is needed:
4041
+ - 'vector': For semantic search, finding specific documents or content based on similarity.
4042
+ - 'graph': For finding relationships between entities, multi-hop questions, or network structures.
4043
+ - 'both': If the query requires both specific content and relationship/structural analysis.
4044
+
4045
+ Query: "${query}"
4046
+
4047
+ Return ONLY 'vector', 'graph', or 'both'. No explanation.`;
4048
+ const response = await llm.chat(
4049
+ [{ role: "user", content: prompt }],
4050
+ "",
4051
+ { temperature: 0, maxTokens: 10 }
4052
+ );
4053
+ const cleanResponse = response.trim().toLowerCase();
4054
+ if (cleanResponse.includes("vector")) return "vector";
4055
+ if (cleanResponse.includes("graph")) return "graph";
4056
+ if (cleanResponse.includes("both")) return "both";
4057
+ } catch (error) {
4058
+ console.warn("[QueryProcessor] LLM strategy classification failed, falling back to keywords:", error);
4059
+ }
4060
+ }
4061
+ const normalized = query.toLowerCase();
4062
+ const graphKeywords = customGraphKeywords || [
4063
+ "relationship",
4064
+ "connect",
4065
+ "link",
4066
+ "network",
4067
+ "friend",
4068
+ "colleague",
4069
+ "manager",
4070
+ "hierarchy",
4071
+ "multi-hop",
4072
+ "shortest path",
4073
+ "between",
4074
+ "related to",
4075
+ "associated with",
4076
+ "belongs to",
4077
+ "part of"
4078
+ ];
4079
+ const hasGraphKeyword = graphKeywords.some((kw) => normalized.includes(kw));
4080
+ const vectorKeywords = customVectorKeywords || [
4081
+ "find",
4082
+ "search",
4083
+ "tell me about",
4084
+ "what is",
4085
+ "how to",
4086
+ "documents about"
4087
+ ];
4088
+ const hasVectorKeyword = vectorKeywords.some((kw) => normalized.includes(kw));
4089
+ if (hasGraphKeyword && !hasVectorKeyword) {
4090
+ return "graph";
4091
+ } else if (hasGraphKeyword && hasVectorKeyword) {
4092
+ return "both";
4093
+ }
4094
+ return "vector";
4095
+ }
3975
4096
  };
3976
4097
 
3977
4098
  // src/utils/synonyms.ts
@@ -5081,6 +5202,7 @@ var Pipeline = class {
5081
5202
  let context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
5082
5203
  ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
5083
5204
  if (graphData && graphData.nodes.length > 0) {
5205
+ console.log(`[Graph Retrieval] Found ${graphData.nodes.length} relevant entities.`);
5084
5206
  const graphContext = graphData.nodes.map(
5085
5207
  (n) => `Entity: ${n.label} (${n.id})${n.properties ? " - " + JSON.stringify(n.properties) : ""}`
5086
5208
  ).join("\n");
@@ -5146,7 +5268,7 @@ ${context}`;
5146
5268
  };
5147
5269
  const hallucinationResult = yield new __await(scoreHallucination(this.llmProvider, fullReply, context).catch(() => void 0));
5148
5270
  const trainedSchema = yield new __await(trainingPromise);
5149
- const uiTransformation = yield new __await(this.generateUiTransformation(question, sources, context, trainedSchema));
5271
+ const uiTransformation = yield new __await(this.generateUiTransformation(question, sources, trainedSchema));
5150
5272
  const trace = {
5151
5273
  requestId,
5152
5274
  query: question,
@@ -5185,7 +5307,7 @@ ${context}`;
5185
5307
  * Universal retrieval method combining all enabled providers.
5186
5308
  * Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
5187
5309
  */
5188
- async generateUiTransformation(question, sources, _context, trainedSchema) {
5310
+ async generateUiTransformation(question, sources, trainedSchema) {
5189
5311
  if (!sources || sources.length === 0) {
5190
5312
  return UITransformer.transform(question, sources, this.config, trainedSchema);
5191
5313
  }
@@ -5202,15 +5324,19 @@ ${context}`;
5202
5324
  const topK = (_b = options.topK) != null ? _b : 5;
5203
5325
  const cacheKey = `${ns}::${query}`;
5204
5326
  let queryVector = this.embeddingCache.get(cacheKey);
5327
+ const strategy = await QueryProcessor.determineRetrievalStrategy(query, this.llmProvider);
5328
+ console.debug(`[Pipeline] Determined retrieval strategy: ${strategy}`);
5205
5329
  const [retrievedVector, graphData] = await Promise.all([
5206
- queryVector ? Promise.resolve(queryVector) : this.embeddingProvider.embed(query, { taskType: "query" }),
5207
- this.graphDB && ((_c = this.config.rag) == null ? void 0 : _c.useGraphRetrieval) ? this.graphDB.query(query) : Promise.resolve(void 0)
5330
+ // Only embed if we need vector search (strategy is 'vector' or 'both')
5331
+ strategy === "vector" || strategy === "both" ? queryVector ? Promise.resolve(queryVector) : this.embeddingProvider.embed(query, { taskType: "query" }) : Promise.resolve([]),
5332
+ // Only query graph if we need graph search (strategy is 'graph' or 'both')
5333
+ (strategy === "graph" || strategy === "both") && this.graphDB && ((_c = this.config.rag) == null ? void 0 : _c.useGraphRetrieval) ? this.graphDB.query(query) : Promise.resolve(void 0)
5208
5334
  ]);
5209
- if (!queryVector) {
5335
+ if ((strategy === "vector" || strategy === "both") && !queryVector && retrievedVector && retrievedVector.length > 0) {
5210
5336
  this.embeddingCache.set(cacheKey, retrievedVector);
5211
5337
  queryVector = retrievedVector;
5212
5338
  }
5213
- const sources = await this.vectorDB.query(queryVector, topK, ns, options.filter);
5339
+ const sources = (strategy === "vector" || strategy === "both") && queryVector && queryVector.length > 0 ? await this.vectorDB.query(queryVector, topK, ns, options.filter) : [];
5214
5340
  return { sources, graphData };
5215
5341
  }
5216
5342
  /** Rewrite the user query for better retrieval performance. */
@@ -5239,13 +5365,13 @@ Optimized Search Query:`;
5239
5365
  await this.initialize();
5240
5366
  const ns = namespace != null ? namespace : this.config.projectId;
5241
5367
  try {
5242
- const { sources } = await this.retrieve(query, { namespace: ns, topK: 3 });
5368
+ const { sources } = await this.retrieve(query, { namespace: ns, topK: 5 });
5243
5369
  if (sources.length === 0) return [];
5244
5370
  const context = sources.map((s) => s.content).join("\n\n---\n\n");
5245
- const prompt = `Based on the following snippets from a document, what are 3 short, relevant questions a user might ask?
5371
+ const prompt = `Based on the following snippets from a document, what are at least 5 short, relevant questions a user might ask?
5246
5372
  Focus on questions that can be answered by the context.
5247
5373
  Keep each question under 10 words and make them very specific to the content.
5248
- Return ONLY a JSON array of strings like ["Question 1", "Question 2", "Question 3"].
5374
+ Return ONLY a JSON array of strings like ["Question 1", "Question 2", "Question 3", "Question 4", "Question 5"].
5249
5375
 
5250
5376
  Context:
5251
5377
  ${context}
@@ -5262,7 +5388,7 @@ Suggestions:`;
5262
5388
  if (match) {
5263
5389
  const suggestions = JSON.parse(match[0]);
5264
5390
  if (Array.isArray(suggestions)) {
5265
- return suggestions.map((s) => String(s)).slice(0, 3);
5391
+ return suggestions.map((s) => String(s)).slice(0, 5);
5266
5392
  }
5267
5393
  }
5268
5394
  } catch (error) {