@retrivora-ai/rag-engine 2.0.8 → 2.1.1

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/index.mjs CHANGED
@@ -1741,32 +1741,31 @@ function isLikelyUiOnlyMessage(text) {
1741
1741
  }
1742
1742
  function extractProductsFromSources(sources, isUser) {
1743
1743
  if (isUser || !sources) return [];
1744
- return sources.filter((s) => {
1745
- var _a;
1744
+ return sources.filter((s) => Boolean(s && typeof s === "object")).filter((s) => {
1745
+ var _a, _b;
1746
1746
  const m = (_a = s.metadata) != null ? _a : {};
1747
1747
  const keys = Object.keys(m).map((k) => k.toLowerCase());
1748
1748
  const hasStrongProductKey = keys.some(
1749
1749
  (k) => ["price", "image", "img", "thumbnail", "images", "product", "sku", "cost"].includes(k)
1750
1750
  );
1751
1751
  const hasProductIdentity = keys.some((k) => ["brand", "model"].includes(k)) && keys.some((k) => ["name", "title", "product_name", "product"].includes(k));
1752
- const hasPricePattern = /\$\s*\d+/.test(s.content);
1752
+ const hasPricePattern = /\$\s*\d+/.test((_b = s == null ? void 0 : s.content) != null ? _b : "");
1753
1753
  return hasStrongProductKey || hasProductIdentity || hasPricePattern || m.type === "product";
1754
1754
  }).map((s) => {
1755
- var _a, _b, _c;
1755
+ var _a, _b, _c, _d;
1756
1756
  const m = (_a = s.metadata) != null ? _a : {};
1757
1757
  const name = resolveMetadataValue(m, "name");
1758
1758
  const brand = resolveMetadataValue(m, "brand");
1759
1759
  const price = resolveMetadataValue(m, "price");
1760
1760
  const description = resolveMetadataValue(m, "description");
1761
1761
  return {
1762
- id: s.id,
1763
- name: (_c = name != null ? name : ((_b = s == null ? void 0 : s.content) != null ? _b : "").split("\n")[0]) != null ? _c : "Unknown Product",
1762
+ id: (_b = s == null ? void 0 : s.id) != null ? _b : `prod_${Math.random().toString(36).substring(2, 8)}`,
1763
+ name: (_d = name != null ? name : ((_c = s == null ? void 0 : s.content) != null ? _c : "").split("\n")[0]) != null ? _d : "Unknown Product",
1764
1764
  brand,
1765
1765
  price,
1766
1766
  image: resolveImage(m),
1767
1767
  link: resolveMetadataValue(m, "link"),
1768
1768
  description: description ? description : ""
1769
- //description ?? s.content,
1770
1769
  };
1771
1770
  });
1772
1771
  }
@@ -3144,10 +3143,10 @@ function ChatWindow({
3144
3143
  ] })
3145
3144
  ] }),
3146
3145
  /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5", children: [
3147
- /* @__PURE__ */ jsxs12("span", { className: "flex items-center gap-1 text-[10px] text-emerald-500 dark:text-emerald-400 mr-2", children: [
3146
+ /* @__PURE__ */ jsx13("div", { className: "flex items-center gap-1.5 mr-2", children: /* @__PURE__ */ jsxs12("span", { className: "flex items-center gap-1 text-[10px] text-emerald-500 dark:text-emerald-400", children: [
3148
3147
  /* @__PURE__ */ jsx13("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-500 dark:bg-emerald-400 animate-pulse" }),
3149
3148
  "Online"
3150
- ] }),
3149
+ ] }) }),
3151
3150
  /* @__PURE__ */ jsxs12("div", { className: "flex items-center bg-slate-100/50 dark:bg-white/5 rounded-lg p-0.5 border border-slate-200/50 dark:border-white/5", children: [
3152
3151
  mounted && messages.length > 0 && /* @__PURE__ */ jsx13(
3153
3152
  "button",
@@ -3675,8 +3674,8 @@ var IntentClassifier = class {
3675
3674
  }
3676
3675
  // ─── Intent Heuristic Checkers ─────────────────────────────────────────────
3677
3676
  static isInformationLookup(query) {
3678
- 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)) {
3679
- if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top selling|best)\b/i.test(query)) {
3677
+ 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)) {
3678
+ if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top 10|top selling|best|chart|graph|plot|breakdown|analytics)\b/i.test(query)) {
3680
3679
  return true;
3681
3680
  }
3682
3681
  }
@@ -3713,7 +3712,7 @@ var Rule1SpecificInfoRule = class {
3713
3712
  }
3714
3713
  evaluate(context, intent) {
3715
3714
  const q = (context.userQuery || "").toLowerCase().trim();
3716
- 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);
3715
+ 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);
3717
3716
  const isNonVisual = !/\b(compare|versus|vs\.?|trend|monthly|revenue|best|top 10|chart|table|list|grid)\b/i.test(q);
3718
3717
  if (isFactQuery && isNonVisual) {
3719
3718
  return {
@@ -4851,6 +4850,11 @@ ${schemaProfileText}` : ""}`;
4851
4850
  }
4852
4851
  static chooseAutomaticVisualization(data, profile, query) {
4853
4852
  if (profile.records.length === 0) return null;
4853
+ const q = query.toLowerCase().trim();
4854
+ 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);
4855
+ if (!isExplicitVisualOrAnalytic) {
4856
+ return null;
4857
+ }
4854
4858
  if (profile.dateFields.length > 0 && profile.numericFields.length > 0) {
4855
4859
  return this.transformToLineChart(profile);
4856
4860
  }
@@ -4966,6 +4970,7 @@ ${schemaProfileText}` : ""}`;
4966
4970
  return Array.from(categories);
4967
4971
  }
4968
4972
  static getProductCategory(item) {
4973
+ if (!item) return null;
4969
4974
  const meta = item.metadata || {};
4970
4975
  const metadataCategory = resolveMetadataValue(meta, "category");
4971
4976
  if (this.isUsableCategory(metadataCategory)) return String(metadataCategory).trim();
@@ -5082,6 +5087,7 @@ ${schemaProfileText}` : ""}`;
5082
5087
  if (!fields.has(normalized)) fields.set(normalized, clean);
5083
5088
  };
5084
5089
  data.forEach((item) => {
5090
+ if (!item) return;
5085
5091
  Object.keys(item.metadata || {}).forEach(addField);
5086
5092
  for (const match of (item.content || "").matchAll(/(?:^|\n)\s*([A-Za-z][A-Za-z0-9_ /-]{1,50})\s*[:\-–]\s*([^\n]+)/g)) {
5087
5093
  addField(match[1]);
@@ -5198,6 +5204,7 @@ ${schemaProfileText}` : ""}`;
5198
5204
  return true;
5199
5205
  }
5200
5206
  static extractStockQuantity(item) {
5207
+ if (!item) return null;
5201
5208
  const meta = item.metadata || {};
5202
5209
  const stockValue = resolveMetadataValue(meta, "stock");
5203
5210
  const numericStock = this.toFiniteNumber(stockValue);
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,
@@ -4623,12 +4637,16 @@ var UniversalLLMAdapter = class {
4623
4637
  async chat(messages, context) {
4624
4638
  var _a2, _b, _c;
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) {
@@ -4700,12 +4718,16 @@ ${context != null ? context : "None"}` },
4700
4718
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
4701
4719
  const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
4702
4720
  const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
4721
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4722
+ role: m.role || "user",
4723
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4724
+ }));
4703
4725
  const formattedMessages = [
4704
4726
  { role: "system", content: `${this.systemPrompt}
4705
4727
 
4706
4728
  Context:
4707
4729
  ${context != null ? context : "None"}` },
4708
- ...messages
4730
+ ...safeMessages
4709
4731
  ];
4710
4732
  let payload;
4711
4733
  if (this.opts.chatPayloadTemplate) {
@@ -6849,8 +6871,8 @@ var IntentClassifier = class {
6849
6871
  }
6850
6872
  // ─── Intent Heuristic Checkers ─────────────────────────────────────────────
6851
6873
  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)) {
6874
+ 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)) {
6875
+ if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top 10|top selling|best|chart|graph|plot|breakdown|analytics)\b/i.test(query)) {
6854
6876
  return true;
6855
6877
  }
6856
6878
  }
@@ -6887,7 +6909,7 @@ var Rule1SpecificInfoRule = class {
6887
6909
  }
6888
6910
  evaluate(context, intent) {
6889
6911
  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);
6912
+ 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
6913
  const isNonVisual = !/\b(compare|versus|vs\.?|trend|monthly|revenue|best|top 10|chart|table|list|grid)\b/i.test(q);
6892
6914
  if (isFactQuery && isNonVisual) {
6893
6915
  return {
@@ -8174,6 +8196,11 @@ ${schemaProfileText}` : ""}`;
8174
8196
  }
8175
8197
  static chooseAutomaticVisualization(data, profile, query) {
8176
8198
  if (profile.records.length === 0) return null;
8199
+ const q = query.toLowerCase().trim();
8200
+ 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);
8201
+ if (!isExplicitVisualOrAnalytic) {
8202
+ return null;
8203
+ }
8177
8204
  if (profile.dateFields.length > 0 && profile.numericFields.length > 0) {
8178
8205
  return this.transformToLineChart(profile);
8179
8206
  }
@@ -8289,6 +8316,7 @@ ${schemaProfileText}` : ""}`;
8289
8316
  return Array.from(categories);
8290
8317
  }
8291
8318
  static getProductCategory(item) {
8319
+ if (!item) return null;
8292
8320
  const meta = item.metadata || {};
8293
8321
  const metadataCategory = resolveMetadataValue(meta, "category");
8294
8322
  if (this.isUsableCategory(metadataCategory)) return String(metadataCategory).trim();
@@ -8405,6 +8433,7 @@ ${schemaProfileText}` : ""}`;
8405
8433
  if (!fields.has(normalized)) fields.set(normalized, clean);
8406
8434
  };
8407
8435
  data.forEach((item) => {
8436
+ if (!item) return;
8408
8437
  Object.keys(item.metadata || {}).forEach(addField);
8409
8438
  for (const match of (item.content || "").matchAll(/(?:^|\n)\s*([A-Za-z][A-Za-z0-9_ /-]{1,50})\s*[:\-–]\s*([^\n]+)/g)) {
8410
8439
  addField(match[1]);
@@ -8521,6 +8550,7 @@ ${schemaProfileText}` : ""}`;
8521
8550
  return true;
8522
8551
  }
8523
8552
  static extractStockQuantity(item) {
8553
+ if (!item) return null;
8524
8554
  const meta = item.metadata || {};
8525
8555
  const stockValue = resolveMetadataValue(meta, "stock");
8526
8556
  const numericStock = this.toFiniteNumber(stockValue);
@@ -9781,7 +9811,10 @@ ${context}`;
9781
9811
  Focus on extracting the core intent and entities. Do not answer the question, just rewrite it.
9782
9812
 
9783
9813
  History:
9784
- ${history.map((m) => `${m.role}: ${m.content}`).join("\n")}
9814
+ ${(history || []).map((m) => {
9815
+ var _a2;
9816
+ return `${(m == null ? void 0 : m.role) || "user"}: ${(_a2 = m == null ? void 0 : m.content) != null ? _a2 : ""}`;
9817
+ }).join("\n")}
9785
9818
 
9786
9819
  New Question: ${question}
9787
9820
 
@@ -9805,8 +9838,11 @@ Optimized Search Query:`;
9805
9838
  const ns = formatNamespace(namespace != null ? namespace : this.config.projectId);
9806
9839
  try {
9807
9840
  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");
9841
+ if (!sources || sources.length === 0) return [];
9842
+ const context = sources.map((s) => {
9843
+ var _a2;
9844
+ return (_a2 = s == null ? void 0 : s.content) != null ? _a2 : "";
9845
+ }).join("\n\n---\n\n");
9810
9846
  const prompt = `Based on the following snippets from a document, what are at least 5 short, relevant questions a user might ask?
9811
9847
  Focus on questions that can be answered by the context.
9812
9848
  Keep each question under 10 words and make them very specific to the content.
package/dist/server.mjs CHANGED
@@ -579,6 +579,7 @@ function cleanApiKeyOverride(apiKeyOverride) {
579
579
  async function dispatchChatCompletion(req, apiKeyOverride) {
580
580
  const effectiveKey = cleanApiKeyOverride(apiKeyOverride);
581
581
  const provider = resolveProvider(req.model);
582
+ 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)}`);
582
583
  try {
583
584
  switch (provider) {
584
585
  case "groq":
@@ -597,6 +598,10 @@ async function dispatchChatCompletion(req, apiKeyOverride) {
597
598
  throw new Error(`Unsupported LLM provider for model: ${req.model}`);
598
599
  }
599
600
  } catch (error) {
601
+ console.error(`[LLM Gateway Router] Provider '${provider}' failed for model '${req.model}':`, {
602
+ message: error.message,
603
+ stack: error.stack
604
+ });
600
605
  if (/rate[- ]?limit|429|exhausted/i.test(error.message)) {
601
606
  const isGroqKey = process.env.GROQ_API_KEY || effectiveKey && effectiveKey.startsWith("gsk_");
602
607
  const reqModelLower = req.model.toLowerCase();
@@ -604,7 +609,8 @@ async function dispatchChatCompletion(req, apiKeyOverride) {
604
609
  console.warn(`[LLM Gateway] Provider ${provider} rate limited (${error.message}). Falling back to secondary Groq model (llama-3.3-70b-versatile)...`);
605
610
  try {
606
611
  return await handleGroqRequest(__spreadProps(__spreadValues({}, req), { model: "llama-3.3-70b-versatile" }), effectiveKey);
607
- } catch (e) {
612
+ } catch (fErr) {
613
+ console.error("[LLM Gateway] Fallback groq/llama-3.3-70b-versatile failed:", fErr.message);
608
614
  }
609
615
  }
610
616
  const geminiKey = process.env.GEMINI_API_KEY || effectiveKey;
@@ -612,14 +618,16 @@ async function dispatchChatCompletion(req, apiKeyOverride) {
612
618
  console.warn(`[LLM Gateway] Provider ${provider} rate limited (${error.message}). Falling back to Google Gemini (gemini-2.5-flash)...`);
613
619
  try {
614
620
  return await handleGeminiRequest(__spreadProps(__spreadValues({}, req), { model: "gemini-2.5-flash" }), effectiveKey);
615
- } catch (e) {
621
+ } catch (fErr) {
622
+ console.error("[LLM Gateway] Fallback gemini-2.5-flash failed:", fErr.message);
616
623
  }
617
624
  }
618
625
  if (provider !== "huggingface" && (process.env.HF_TOKEN || process.env.HUGGINGFACE_API_KEY)) {
619
626
  console.warn(`[LLM Gateway] Provider ${provider} rate limited (${error.message}). Falling back to Hugging Face (Qwen/Qwen2.5-Coder-32B-Instruct)...`);
620
627
  try {
621
628
  return await handleHuggingFaceChatRequest(__spreadProps(__spreadValues({}, req), { model: "Qwen/Qwen2.5-Coder-32B-Instruct" }), effectiveKey);
622
- } catch (e) {
629
+ } catch (fErr) {
630
+ console.error("[LLM Gateway] Fallback HuggingFace Qwen failed:", fErr.message);
623
631
  }
624
632
  }
625
633
  if (provider !== "openai" && process.env.OPENAI_API_KEY) {
@@ -3393,12 +3401,13 @@ var OpenAIProvider = class {
3393
3401
  role: "system",
3394
3402
  content: buildSystemContent(basePrompt, context)
3395
3403
  };
3404
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
3405
+ role: m.role || "user",
3406
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
3407
+ }));
3396
3408
  const formattedMessages = [
3397
3409
  systemMessage,
3398
- ...messages.map((m) => ({
3399
- role: m.role,
3400
- content: m.content
3401
- }))
3410
+ ...safeMessages
3402
3411
  ];
3403
3412
  const completion = await this.client.chat.completions.create({
3404
3413
  model: this.llmConfig.model,
@@ -3417,12 +3426,13 @@ var OpenAIProvider = class {
3417
3426
  role: "system",
3418
3427
  content: buildSystemContent(basePrompt, context)
3419
3428
  };
3429
+ const safeStreamMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
3430
+ role: m.role || "user",
3431
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
3432
+ }));
3420
3433
  const formattedMessages = [
3421
3434
  systemMessage,
3422
- ...messages.map((m) => ({
3423
- role: m.role,
3424
- content: m.content
3425
- }))
3435
+ ...safeStreamMessages
3426
3436
  ];
3427
3437
  const stream = yield new __await(this.client.chat.completions.create({
3428
3438
  model: this.llmConfig.model,
@@ -3953,9 +3963,9 @@ var GeminiProvider = class {
3953
3963
  * - Messages must strictly alternate between `user` and `model`.
3954
3964
  */
3955
3965
  buildGeminiContents(messages) {
3956
- return messages.filter((m) => m.role !== "system").map((m) => ({
3966
+ return (messages || []).filter((m) => Boolean(m && typeof m === "object" && m.role !== "system")).map((m) => ({
3957
3967
  role: m.role === "assistant" ? "model" : "user",
3958
- parts: [{ text: m.content }]
3968
+ parts: [{ text: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : "" }]
3959
3969
  }));
3960
3970
  }
3961
3971
  // -------------------------------------------------------------------------
@@ -4158,12 +4168,13 @@ var GroqProvider = class {
4158
4168
  role: "system",
4159
4169
  content: buildSystemContent(basePrompt, context)
4160
4170
  };
4171
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4172
+ role: m.role || "user",
4173
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4174
+ }));
4161
4175
  const formattedMessages = [
4162
4176
  systemMessage,
4163
- ...messages.map((m) => ({
4164
- role: m.role,
4165
- content: m.content
4166
- }))
4177
+ ...safeMessages
4167
4178
  ];
4168
4179
  const completion = await this.client.chat.completions.create({
4169
4180
  model: this.llmConfig.model,
@@ -4182,12 +4193,13 @@ var GroqProvider = class {
4182
4193
  role: "system",
4183
4194
  content: buildSystemContent(basePrompt, context)
4184
4195
  };
4196
+ const safeStreamMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4197
+ role: m.role || "user",
4198
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4199
+ }));
4185
4200
  const formattedMessages = [
4186
4201
  systemMessage,
4187
- ...messages.map((m) => ({
4188
- role: m.role,
4189
- content: m.content
4190
- }))
4202
+ ...safeStreamMessages
4191
4203
  ];
4192
4204
  const stream = yield new __await(this.client.chat.completions.create({
4193
4205
  model: this.llmConfig.model,
@@ -4316,12 +4328,13 @@ var QwenProvider = class {
4316
4328
  role: "system",
4317
4329
  content: buildSystemContent(basePrompt, context)
4318
4330
  };
4331
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4332
+ role: m.role || "user",
4333
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4334
+ }));
4319
4335
  const formattedMessages = [
4320
4336
  systemMessage,
4321
- ...messages.map((m) => ({
4322
- role: m.role,
4323
- content: m.content
4324
- }))
4337
+ ...safeMessages
4325
4338
  ];
4326
4339
  const completion = await this.client.chat.completions.create({
4327
4340
  model: this.llmConfig.model,
@@ -4340,12 +4353,13 @@ var QwenProvider = class {
4340
4353
  role: "system",
4341
4354
  content: buildSystemContent(basePrompt, context)
4342
4355
  };
4356
+ const safeStreamMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4357
+ role: m.role || "user",
4358
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4359
+ }));
4343
4360
  const formattedMessages = [
4344
4361
  systemMessage,
4345
- ...messages.map((m) => ({
4346
- role: m.role,
4347
- content: m.content
4348
- }))
4362
+ ...safeStreamMessages
4349
4363
  ];
4350
4364
  const stream = yield new __await(this.client.chat.completions.create({
4351
4365
  model: this.llmConfig.model,
@@ -4527,12 +4541,16 @@ var UniversalLLMAdapter = class {
4527
4541
  async chat(messages, context) {
4528
4542
  var _a2, _b, _c;
4529
4543
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
4544
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4545
+ role: m.role || "user",
4546
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4547
+ }));
4530
4548
  const formattedMessages = [
4531
4549
  { role: "system", content: `${this.systemPrompt}
4532
4550
 
4533
4551
  Context:
4534
4552
  ${context != null ? context : "None"}` },
4535
- ...messages
4553
+ ...safeMessages
4536
4554
  ];
4537
4555
  let payload;
4538
4556
  if (this.opts.chatPayloadTemplate) {
@@ -4604,12 +4622,16 @@ ${context != null ? context : "None"}` },
4604
4622
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
4605
4623
  const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
4606
4624
  const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
4625
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4626
+ role: m.role || "user",
4627
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4628
+ }));
4607
4629
  const formattedMessages = [
4608
4630
  { role: "system", content: `${this.systemPrompt}
4609
4631
 
4610
4632
  Context:
4611
4633
  ${context != null ? context : "None"}` },
4612
- ...messages
4634
+ ...safeMessages
4613
4635
  ];
4614
4636
  let payload;
4615
4637
  if (this.opts.chatPayloadTemplate) {
@@ -6753,8 +6775,8 @@ var IntentClassifier = class {
6753
6775
  }
6754
6776
  // ─── Intent Heuristic Checkers ─────────────────────────────────────────────
6755
6777
  static isInformationLookup(query) {
6756
- 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)) {
6757
- if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top selling|best)\b/i.test(query)) {
6778
+ 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)) {
6779
+ if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top 10|top selling|best|chart|graph|plot|breakdown|analytics)\b/i.test(query)) {
6758
6780
  return true;
6759
6781
  }
6760
6782
  }
@@ -6791,7 +6813,7 @@ var Rule1SpecificInfoRule = class {
6791
6813
  }
6792
6814
  evaluate(context, intent) {
6793
6815
  const q = (context.userQuery || "").toLowerCase().trim();
6794
- 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);
6816
+ 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);
6795
6817
  const isNonVisual = !/\b(compare|versus|vs\.?|trend|monthly|revenue|best|top 10|chart|table|list|grid)\b/i.test(q);
6796
6818
  if (isFactQuery && isNonVisual) {
6797
6819
  return {
@@ -8078,6 +8100,11 @@ ${schemaProfileText}` : ""}`;
8078
8100
  }
8079
8101
  static chooseAutomaticVisualization(data, profile, query) {
8080
8102
  if (profile.records.length === 0) return null;
8103
+ const q = query.toLowerCase().trim();
8104
+ 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);
8105
+ if (!isExplicitVisualOrAnalytic) {
8106
+ return null;
8107
+ }
8081
8108
  if (profile.dateFields.length > 0 && profile.numericFields.length > 0) {
8082
8109
  return this.transformToLineChart(profile);
8083
8110
  }
@@ -8193,6 +8220,7 @@ ${schemaProfileText}` : ""}`;
8193
8220
  return Array.from(categories);
8194
8221
  }
8195
8222
  static getProductCategory(item) {
8223
+ if (!item) return null;
8196
8224
  const meta = item.metadata || {};
8197
8225
  const metadataCategory = resolveMetadataValue(meta, "category");
8198
8226
  if (this.isUsableCategory(metadataCategory)) return String(metadataCategory).trim();
@@ -8309,6 +8337,7 @@ ${schemaProfileText}` : ""}`;
8309
8337
  if (!fields.has(normalized)) fields.set(normalized, clean);
8310
8338
  };
8311
8339
  data.forEach((item) => {
8340
+ if (!item) return;
8312
8341
  Object.keys(item.metadata || {}).forEach(addField);
8313
8342
  for (const match of (item.content || "").matchAll(/(?:^|\n)\s*([A-Za-z][A-Za-z0-9_ /-]{1,50})\s*[:\-–]\s*([^\n]+)/g)) {
8314
8343
  addField(match[1]);
@@ -8425,6 +8454,7 @@ ${schemaProfileText}` : ""}`;
8425
8454
  return true;
8426
8455
  }
8427
8456
  static extractStockQuantity(item) {
8457
+ if (!item) return null;
8428
8458
  const meta = item.metadata || {};
8429
8459
  const stockValue = resolveMetadataValue(meta, "stock");
8430
8460
  const numericStock = this.toFiniteNumber(stockValue);
@@ -9685,7 +9715,10 @@ ${context}`;
9685
9715
  Focus on extracting the core intent and entities. Do not answer the question, just rewrite it.
9686
9716
 
9687
9717
  History:
9688
- ${history.map((m) => `${m.role}: ${m.content}`).join("\n")}
9718
+ ${(history || []).map((m) => {
9719
+ var _a2;
9720
+ return `${(m == null ? void 0 : m.role) || "user"}: ${(_a2 = m == null ? void 0 : m.content) != null ? _a2 : ""}`;
9721
+ }).join("\n")}
9689
9722
 
9690
9723
  New Question: ${question}
9691
9724
 
@@ -9709,8 +9742,11 @@ Optimized Search Query:`;
9709
9742
  const ns = formatNamespace(namespace != null ? namespace : this.config.projectId);
9710
9743
  try {
9711
9744
  const { sources } = await this.retrieve(query, { namespace: ns, topK: 5 });
9712
- if (sources.length === 0) return [];
9713
- const context = sources.map((s) => s.content).join("\n\n---\n\n");
9745
+ if (!sources || sources.length === 0) return [];
9746
+ const context = sources.map((s) => {
9747
+ var _a2;
9748
+ return (_a2 = s == null ? void 0 : s.content) != null ? _a2 : "";
9749
+ }).join("\n\n---\n\n");
9714
9750
  const prompt = `Based on the following snippets from a document, what are at least 5 short, relevant questions a user might ask?
9715
9751
  Focus on questions that can be answered by the context.
9716
9752
  Keep each question under 10 words and make them very specific to the content.