@retrivora-ai/rag-engine 2.0.8 → 2.1.2

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/dist/server.js CHANGED
@@ -594,6 +594,7 @@ function cleanApiKeyOverride(apiKeyOverride) {
594
594
  async function dispatchChatCompletion(req, apiKeyOverride) {
595
595
  const effectiveKey = cleanApiKeyOverride(apiKeyOverride);
596
596
  const provider = resolveProvider(req.model);
597
+ console.log(`[LLM Gateway Router] Dispatching chat request: model=${req.model}, provider=${provider}, hasGroqKey=${Boolean(process.env.GROQ_API_KEY)}, hasGeminiKey=${Boolean(process.env.GROQ_API_KEY || process.env.GEMINI_API_KEY)}, hasHFToken=${Boolean(process.env.HF_TOKEN)}`);
597
598
  try {
598
599
  switch (provider) {
599
600
  case "groq":
@@ -612,6 +613,10 @@ async function dispatchChatCompletion(req, apiKeyOverride) {
612
613
  throw new Error(`Unsupported LLM provider for model: ${req.model}`);
613
614
  }
614
615
  } catch (error) {
616
+ console.error(`[LLM Gateway Router] Provider '${provider}' failed for model '${req.model}':`, {
617
+ message: error.message,
618
+ stack: error.stack
619
+ });
615
620
  if (/rate[- ]?limit|429|exhausted/i.test(error.message)) {
616
621
  const isGroqKey = process.env.GROQ_API_KEY || effectiveKey && effectiveKey.startsWith("gsk_");
617
622
  const reqModelLower = req.model.toLowerCase();
@@ -619,7 +624,8 @@ async function dispatchChatCompletion(req, apiKeyOverride) {
619
624
  console.warn(`[LLM Gateway] Provider ${provider} rate limited (${error.message}). Falling back to secondary Groq model (llama-3.3-70b-versatile)...`);
620
625
  try {
621
626
  return await handleGroqRequest(__spreadProps(__spreadValues({}, req), { model: "llama-3.3-70b-versatile" }), effectiveKey);
622
- } catch (e) {
627
+ } catch (fErr) {
628
+ console.error("[LLM Gateway] Fallback groq/llama-3.3-70b-versatile failed:", fErr.message);
623
629
  }
624
630
  }
625
631
  const geminiKey = process.env.GEMINI_API_KEY || effectiveKey;
@@ -627,14 +633,16 @@ async function dispatchChatCompletion(req, apiKeyOverride) {
627
633
  console.warn(`[LLM Gateway] Provider ${provider} rate limited (${error.message}). Falling back to Google Gemini (gemini-2.5-flash)...`);
628
634
  try {
629
635
  return await handleGeminiRequest(__spreadProps(__spreadValues({}, req), { model: "gemini-2.5-flash" }), effectiveKey);
630
- } catch (e) {
636
+ } catch (fErr) {
637
+ console.error("[LLM Gateway] Fallback gemini-2.5-flash failed:", fErr.message);
631
638
  }
632
639
  }
633
640
  if (provider !== "huggingface" && (process.env.HF_TOKEN || process.env.HUGGINGFACE_API_KEY)) {
634
641
  console.warn(`[LLM Gateway] Provider ${provider} rate limited (${error.message}). Falling back to Hugging Face (Qwen/Qwen2.5-Coder-32B-Instruct)...`);
635
642
  try {
636
643
  return await handleHuggingFaceChatRequest(__spreadProps(__spreadValues({}, req), { model: "Qwen/Qwen2.5-Coder-32B-Instruct" }), effectiveKey);
637
- } catch (e) {
644
+ } catch (fErr) {
645
+ console.error("[LLM Gateway] Fallback HuggingFace Qwen failed:", fErr.message);
638
646
  }
639
647
  }
640
648
  if (provider !== "openai" && process.env.OPENAI_API_KEY) {
@@ -3489,12 +3497,13 @@ var OpenAIProvider = class {
3489
3497
  role: "system",
3490
3498
  content: buildSystemContent(basePrompt, context)
3491
3499
  };
3500
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
3501
+ role: m.role || "user",
3502
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
3503
+ }));
3492
3504
  const formattedMessages = [
3493
3505
  systemMessage,
3494
- ...messages.map((m) => ({
3495
- role: m.role,
3496
- content: m.content
3497
- }))
3506
+ ...safeMessages
3498
3507
  ];
3499
3508
  const completion = await this.client.chat.completions.create({
3500
3509
  model: this.llmConfig.model,
@@ -3513,12 +3522,13 @@ var OpenAIProvider = class {
3513
3522
  role: "system",
3514
3523
  content: buildSystemContent(basePrompt, context)
3515
3524
  };
3525
+ const safeStreamMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
3526
+ role: m.role || "user",
3527
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
3528
+ }));
3516
3529
  const formattedMessages = [
3517
3530
  systemMessage,
3518
- ...messages.map((m) => ({
3519
- role: m.role,
3520
- content: m.content
3521
- }))
3531
+ ...safeStreamMessages
3522
3532
  ];
3523
3533
  const stream = yield new __await(this.client.chat.completions.create({
3524
3534
  model: this.llmConfig.model,
@@ -4049,9 +4059,9 @@ var GeminiProvider = class {
4049
4059
  * - Messages must strictly alternate between `user` and `model`.
4050
4060
  */
4051
4061
  buildGeminiContents(messages) {
4052
- return messages.filter((m) => m.role !== "system").map((m) => ({
4062
+ return (messages || []).filter((m) => Boolean(m && typeof m === "object" && m.role !== "system")).map((m) => ({
4053
4063
  role: m.role === "assistant" ? "model" : "user",
4054
- parts: [{ text: m.content }]
4064
+ parts: [{ text: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : "" }]
4055
4065
  }));
4056
4066
  }
4057
4067
  // -------------------------------------------------------------------------
@@ -4254,12 +4264,13 @@ var GroqProvider = class {
4254
4264
  role: "system",
4255
4265
  content: buildSystemContent(basePrompt, context)
4256
4266
  };
4267
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4268
+ role: m.role || "user",
4269
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4270
+ }));
4257
4271
  const formattedMessages = [
4258
4272
  systemMessage,
4259
- ...messages.map((m) => ({
4260
- role: m.role,
4261
- content: m.content
4262
- }))
4273
+ ...safeMessages
4263
4274
  ];
4264
4275
  const completion = await this.client.chat.completions.create({
4265
4276
  model: this.llmConfig.model,
@@ -4278,12 +4289,13 @@ var GroqProvider = class {
4278
4289
  role: "system",
4279
4290
  content: buildSystemContent(basePrompt, context)
4280
4291
  };
4292
+ const safeStreamMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4293
+ role: m.role || "user",
4294
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4295
+ }));
4281
4296
  const formattedMessages = [
4282
4297
  systemMessage,
4283
- ...messages.map((m) => ({
4284
- role: m.role,
4285
- content: m.content
4286
- }))
4298
+ ...safeStreamMessages
4287
4299
  ];
4288
4300
  const stream = yield new __await(this.client.chat.completions.create({
4289
4301
  model: this.llmConfig.model,
@@ -4412,12 +4424,13 @@ var QwenProvider = class {
4412
4424
  role: "system",
4413
4425
  content: buildSystemContent(basePrompt, context)
4414
4426
  };
4427
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4428
+ role: m.role || "user",
4429
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4430
+ }));
4415
4431
  const formattedMessages = [
4416
4432
  systemMessage,
4417
- ...messages.map((m) => ({
4418
- role: m.role,
4419
- content: m.content
4420
- }))
4433
+ ...safeMessages
4421
4434
  ];
4422
4435
  const completion = await this.client.chat.completions.create({
4423
4436
  model: this.llmConfig.model,
@@ -4436,12 +4449,13 @@ var QwenProvider = class {
4436
4449
  role: "system",
4437
4450
  content: buildSystemContent(basePrompt, context)
4438
4451
  };
4452
+ const safeStreamMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4453
+ role: m.role || "user",
4454
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4455
+ }));
4439
4456
  const formattedMessages = [
4440
4457
  systemMessage,
4441
- ...messages.map((m) => ({
4442
- role: m.role,
4443
- content: m.content
4444
- }))
4458
+ ...safeStreamMessages
4445
4459
  ];
4446
4460
  const stream = yield new __await(this.client.chat.completions.create({
4447
4461
  model: this.llmConfig.model,
@@ -4621,14 +4635,18 @@ var UniversalLLMAdapter = class {
4621
4635
  });
4622
4636
  }
4623
4637
  async chat(messages, context) {
4624
- var _a2, _b, _c;
4638
+ var _a2, _b, _c, _d, _e;
4625
4639
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
4640
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4641
+ role: m.role || "user",
4642
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4643
+ }));
4626
4644
  const formattedMessages = [
4627
4645
  { role: "system", content: `${this.systemPrompt}
4628
4646
 
4629
4647
  Context:
4630
4648
  ${context != null ? context : "None"}` },
4631
- ...messages
4649
+ ...safeMessages
4632
4650
  ];
4633
4651
  let payload;
4634
4652
  if (this.opts.chatPayloadTemplate) {
@@ -4646,8 +4664,15 @@ ${context != null ? context : "None"}` },
4646
4664
  temperature: this.temperature
4647
4665
  };
4648
4666
  }
4649
- const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
4650
- if (isSelfHost || Boolean(process.env.VERCEL)) {
4667
+ try {
4668
+ const { data: data2 } = await this.http.post(path2, payload);
4669
+ const extractPath2 = (_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content";
4670
+ const result2 = (_c = resolvePath(data2, extractPath2)) != null ? _c : extractContent(data2);
4671
+ if (result2 !== void 0) {
4672
+ return String(result2);
4673
+ }
4674
+ } catch (httpErr) {
4675
+ console.warn(`[UniversalLLMAdapter] Direct HTTP POST to ${this.baseUrl}${path2} failed (${httpErr.message}). Attempting in-process gateway dispatch fallback...`);
4651
4676
  const _g2 = globalThis;
4652
4677
  let dispatch = _g2.__retrivoraDispatchChat;
4653
4678
  if (typeof dispatch !== "function") {
@@ -4662,28 +4687,22 @@ ${context != null ? context : "None"}` },
4662
4687
  }
4663
4688
  }
4664
4689
  if (typeof dispatch === "function") {
4665
- try {
4666
- const res = await dispatch({
4667
- model: this.model,
4668
- messages: formattedMessages,
4669
- max_tokens: this.maxTokens,
4670
- temperature: this.temperature
4671
- }, this.apiKey);
4672
- const content = extractContent(res == null ? void 0 : res.response);
4673
- if (content !== void 0) {
4674
- return content;
4675
- }
4676
- throw new Error(`[UniversalLLMAdapter] In-process dispatch returned empty content for model: ${this.model}`);
4677
- } catch (dispatchErr) {
4678
- throw dispatchErr;
4690
+ const res = await dispatch({
4691
+ model: this.model,
4692
+ messages: formattedMessages,
4693
+ max_tokens: this.maxTokens,
4694
+ temperature: this.temperature
4695
+ }, this.apiKey);
4696
+ const content = extractContent(res == null ? void 0 : res.response);
4697
+ if (content !== void 0) {
4698
+ return content;
4679
4699
  }
4680
- } else {
4681
- throw new Error(`[UniversalLLMAdapter] In-process gateway dispatch not registered. Direct self-referential HTTP calls to ${this.baseUrl} are disabled on Vercel to prevent HTTP 508 Loop Detected.`);
4682
4700
  }
4701
+ throw httpErr;
4683
4702
  }
4684
4703
  const { data } = await this.http.post(path2, payload);
4685
- const extractPath = (_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content";
4686
- const result = (_c = resolvePath(data, extractPath)) != null ? _c : extractContent(data);
4704
+ const extractPath = (_d = this.opts.responseExtractPath) != null ? _d : "choices[0].message.content";
4705
+ const result = (_e = resolvePath(data, extractPath)) != null ? _e : extractContent(data);
4687
4706
  if (result === void 0) {
4688
4707
  throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
4689
4708
  }
@@ -4700,12 +4719,16 @@ ${context != null ? context : "None"}` },
4700
4719
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
4701
4720
  const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
4702
4721
  const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
4722
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4723
+ role: m.role || "user",
4724
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4725
+ }));
4703
4726
  const formattedMessages = [
4704
4727
  { role: "system", content: `${this.systemPrompt}
4705
4728
 
4706
4729
  Context:
4707
4730
  ${context != null ? context : "None"}` },
4708
- ...messages
4731
+ ...safeMessages
4709
4732
  ];
4710
4733
  let payload;
4711
4734
  if (this.opts.chatPayloadTemplate) {
@@ -4821,7 +4844,7 @@ ${context != null ? context : "None"}` },
4821
4844
  });
4822
4845
  }
4823
4846
  async embed(text) {
4824
- var _a2, _b, _c, _d;
4847
+ var _a2, _b, _c, _d, _e;
4825
4848
  const path2 = (_a2 = this.opts.embedPath) != null ? _a2 : "/embeddings";
4826
4849
  let payload;
4827
4850
  if (this.opts.embedPayloadTemplate) {
@@ -4835,8 +4858,15 @@ ${context != null ? context : "None"}` },
4835
4858
  input: text
4836
4859
  };
4837
4860
  }
4838
- const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
4839
- if (isSelfHost || Boolean(process.env.VERCEL)) {
4861
+ try {
4862
+ const { data: data2 } = await this.http.post(path2, payload);
4863
+ const extractPath2 = (_b = this.opts.embedExtractPath) != null ? _b : "data[0].embedding";
4864
+ const vector2 = resolvePath(data2, extractPath2);
4865
+ if (Array.isArray(vector2)) {
4866
+ return vector2;
4867
+ }
4868
+ } catch (httpErr) {
4869
+ console.warn(`[UniversalLLMAdapter] Direct HTTP embedding POST to ${this.baseUrl}${path2} failed (${httpErr.message}). Attempting in-process gateway dispatch fallback...`);
4840
4870
  const _g2 = globalThis;
4841
4871
  let dispatch = _g2.__retrivoraDispatchEmbedding;
4842
4872
  if (typeof dispatch !== "function") {
@@ -4851,24 +4881,18 @@ ${context != null ? context : "None"}` },
4851
4881
  }
4852
4882
  }
4853
4883
  if (typeof dispatch === "function") {
4854
- try {
4855
- const res = await dispatch({
4856
- model: this.model,
4857
- input: text
4858
- }, this.apiKey);
4859
- if ((_c = (_b = res == null ? void 0 : res.data) == null ? void 0 : _b[0]) == null ? void 0 : _c.embedding) {
4860
- return res.data[0].embedding;
4861
- }
4862
- throw new Error(`[UniversalLLMAdapter] In-process embedding dispatch returned invalid vector for model: ${this.model}`);
4863
- } catch (dispatchErr) {
4864
- throw dispatchErr;
4884
+ const res = await dispatch({
4885
+ model: this.model,
4886
+ input: text
4887
+ }, this.apiKey);
4888
+ if ((_d = (_c = res == null ? void 0 : res.data) == null ? void 0 : _c[0]) == null ? void 0 : _d.embedding) {
4889
+ return res.data[0].embedding;
4865
4890
  }
4866
- } else {
4867
- throw new Error(`[UniversalLLMAdapter] In-process gateway dispatch not registered. Direct self-referential HTTP calls to ${this.baseUrl} are disabled on Vercel to prevent HTTP 508 Loop Detected.`);
4868
4891
  }
4892
+ throw httpErr;
4869
4893
  }
4870
4894
  const { data } = await this.http.post(path2, payload);
4871
- const extractPath = (_d = this.opts.embedExtractPath) != null ? _d : "data[0].embedding";
4895
+ const extractPath = (_e = this.opts.embedExtractPath) != null ? _e : "data[0].embedding";
4872
4896
  const vector = resolvePath(data, extractPath);
4873
4897
  if (!Array.isArray(vector)) {
4874
4898
  throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
@@ -6849,8 +6873,8 @@ var IntentClassifier = class {
6849
6873
  }
6850
6874
  // ─── Intent Heuristic Checkers ─────────────────────────────────────────────
6851
6875
  static isInformationLookup(query) {
6852
- 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)) {
6853
- if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top selling|best)\b/i.test(query)) {
6876
+ if (/^(what|who|explain|explore|define|where|when|why|how|how does|can|could|does|do|is|are|has|have|will|should|would|meaning of|tell me about|show details of|overview of|summarize)\s+/i.test(query)) {
6877
+ if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top 10|top selling|best|chart|graph|plot|breakdown|analytics)\b/i.test(query)) {
6854
6878
  return true;
6855
6879
  }
6856
6880
  }
@@ -6887,7 +6911,7 @@ var Rule1SpecificInfoRule = class {
6887
6911
  }
6888
6912
  evaluate(context, intent) {
6889
6913
  const q = (context.userQuery || "").toLowerCase().trim();
6890
- 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);
6914
+ const isFactQuery = intent === "information_lookup" || /^(what|who|explain|explore|define|where|when|why|meaning of|tell me about|show details of|overview of|summarize)\s+/i.test(q) || /^(what is the price of|price of|cost of)\b/i.test(q);
6891
6915
  const isNonVisual = !/\b(compare|versus|vs\.?|trend|monthly|revenue|best|top 10|chart|table|list|grid)\b/i.test(q);
6892
6916
  if (isFactQuery && isNonVisual) {
6893
6917
  return {
@@ -8174,6 +8198,11 @@ ${schemaProfileText}` : ""}`;
8174
8198
  }
8175
8199
  static chooseAutomaticVisualization(data, profile, query) {
8176
8200
  if (profile.records.length === 0) return null;
8201
+ const q = query.toLowerCase().trim();
8202
+ const isExplicitVisualOrAnalytic = /\b(chart|graph|plot|visualize|trend|monthly|revenue|top\s*\d+|breakdown|analytics|compare|versus|vs\.?|histogram|pie|bar|line|distribution|percentage|share|summary of metrics)\b/i.test(q);
8203
+ if (!isExplicitVisualOrAnalytic) {
8204
+ return null;
8205
+ }
8177
8206
  if (profile.dateFields.length > 0 && profile.numericFields.length > 0) {
8178
8207
  return this.transformToLineChart(profile);
8179
8208
  }
@@ -8289,6 +8318,7 @@ ${schemaProfileText}` : ""}`;
8289
8318
  return Array.from(categories);
8290
8319
  }
8291
8320
  static getProductCategory(item) {
8321
+ if (!item) return null;
8292
8322
  const meta = item.metadata || {};
8293
8323
  const metadataCategory = resolveMetadataValue(meta, "category");
8294
8324
  if (this.isUsableCategory(metadataCategory)) return String(metadataCategory).trim();
@@ -8405,6 +8435,7 @@ ${schemaProfileText}` : ""}`;
8405
8435
  if (!fields.has(normalized)) fields.set(normalized, clean);
8406
8436
  };
8407
8437
  data.forEach((item) => {
8438
+ if (!item) return;
8408
8439
  Object.keys(item.metadata || {}).forEach(addField);
8409
8440
  for (const match of (item.content || "").matchAll(/(?:^|\n)\s*([A-Za-z][A-Za-z0-9_ /-]{1,50})\s*[:\-–]\s*([^\n]+)/g)) {
8410
8441
  addField(match[1]);
@@ -8521,6 +8552,7 @@ ${schemaProfileText}` : ""}`;
8521
8552
  return true;
8522
8553
  }
8523
8554
  static extractStockQuantity(item) {
8555
+ if (!item) return null;
8524
8556
  const meta = item.metadata || {};
8525
8557
  const stockValue = resolveMetadataValue(meta, "stock");
8526
8558
  const numericStock = this.toFiniteNumber(stockValue);
@@ -9781,7 +9813,10 @@ ${context}`;
9781
9813
  Focus on extracting the core intent and entities. Do not answer the question, just rewrite it.
9782
9814
 
9783
9815
  History:
9784
- ${history.map((m) => `${m.role}: ${m.content}`).join("\n")}
9816
+ ${(history || []).map((m) => {
9817
+ var _a2;
9818
+ return `${(m == null ? void 0 : m.role) || "user"}: ${(_a2 = m == null ? void 0 : m.content) != null ? _a2 : ""}`;
9819
+ }).join("\n")}
9785
9820
 
9786
9821
  New Question: ${question}
9787
9822
 
@@ -9805,8 +9840,11 @@ Optimized Search Query:`;
9805
9840
  const ns = formatNamespace(namespace != null ? namespace : this.config.projectId);
9806
9841
  try {
9807
9842
  const { sources } = await this.retrieve(query, { namespace: ns, topK: 5 });
9808
- if (sources.length === 0) return [];
9809
- const context = sources.map((s) => s.content).join("\n\n---\n\n");
9843
+ if (!sources || sources.length === 0) return [];
9844
+ const context = sources.map((s) => {
9845
+ var _a2;
9846
+ return (_a2 = s == null ? void 0 : s.content) != null ? _a2 : "";
9847
+ }).join("\n\n---\n\n");
9810
9848
  const prompt = `Based on the following snippets from a document, what are at least 5 short, relevant questions a user might ask?
9811
9849
  Focus on questions that can be answered by the context.
9812
9850
  Keep each question under 10 words and make them very specific to the content.