@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.
@@ -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) {
@@ -3428,12 +3436,13 @@ var OpenAIProvider = class {
3428
3436
  role: "system",
3429
3437
  content: buildSystemContent(basePrompt, context)
3430
3438
  };
3439
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
3440
+ role: m.role || "user",
3441
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
3442
+ }));
3431
3443
  const formattedMessages = [
3432
3444
  systemMessage,
3433
- ...messages.map((m) => ({
3434
- role: m.role,
3435
- content: m.content
3436
- }))
3445
+ ...safeMessages
3437
3446
  ];
3438
3447
  const completion = await this.client.chat.completions.create({
3439
3448
  model: this.llmConfig.model,
@@ -3452,12 +3461,13 @@ var OpenAIProvider = class {
3452
3461
  role: "system",
3453
3462
  content: buildSystemContent(basePrompt, context)
3454
3463
  };
3464
+ const safeStreamMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
3465
+ role: m.role || "user",
3466
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
3467
+ }));
3455
3468
  const formattedMessages = [
3456
3469
  systemMessage,
3457
- ...messages.map((m) => ({
3458
- role: m.role,
3459
- content: m.content
3460
- }))
3470
+ ...safeStreamMessages
3461
3471
  ];
3462
3472
  const stream = yield new __await(this.client.chat.completions.create({
3463
3473
  model: this.llmConfig.model,
@@ -3988,9 +3998,9 @@ var GeminiProvider = class {
3988
3998
  * - Messages must strictly alternate between `user` and `model`.
3989
3999
  */
3990
4000
  buildGeminiContents(messages) {
3991
- return messages.filter((m) => m.role !== "system").map((m) => ({
4001
+ return (messages || []).filter((m) => Boolean(m && typeof m === "object" && m.role !== "system")).map((m) => ({
3992
4002
  role: m.role === "assistant" ? "model" : "user",
3993
- parts: [{ text: m.content }]
4003
+ parts: [{ text: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : "" }]
3994
4004
  }));
3995
4005
  }
3996
4006
  // -------------------------------------------------------------------------
@@ -4193,12 +4203,13 @@ var GroqProvider = class {
4193
4203
  role: "system",
4194
4204
  content: buildSystemContent(basePrompt, context)
4195
4205
  };
4206
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4207
+ role: m.role || "user",
4208
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4209
+ }));
4196
4210
  const formattedMessages = [
4197
4211
  systemMessage,
4198
- ...messages.map((m) => ({
4199
- role: m.role,
4200
- content: m.content
4201
- }))
4212
+ ...safeMessages
4202
4213
  ];
4203
4214
  const completion = await this.client.chat.completions.create({
4204
4215
  model: this.llmConfig.model,
@@ -4217,12 +4228,13 @@ var GroqProvider = class {
4217
4228
  role: "system",
4218
4229
  content: buildSystemContent(basePrompt, context)
4219
4230
  };
4231
+ const safeStreamMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4232
+ role: m.role || "user",
4233
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4234
+ }));
4220
4235
  const formattedMessages = [
4221
4236
  systemMessage,
4222
- ...messages.map((m) => ({
4223
- role: m.role,
4224
- content: m.content
4225
- }))
4237
+ ...safeStreamMessages
4226
4238
  ];
4227
4239
  const stream = yield new __await(this.client.chat.completions.create({
4228
4240
  model: this.llmConfig.model,
@@ -4351,12 +4363,13 @@ var QwenProvider = class {
4351
4363
  role: "system",
4352
4364
  content: buildSystemContent(basePrompt, context)
4353
4365
  };
4366
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4367
+ role: m.role || "user",
4368
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4369
+ }));
4354
4370
  const formattedMessages = [
4355
4371
  systemMessage,
4356
- ...messages.map((m) => ({
4357
- role: m.role,
4358
- content: m.content
4359
- }))
4372
+ ...safeMessages
4360
4373
  ];
4361
4374
  const completion = await this.client.chat.completions.create({
4362
4375
  model: this.llmConfig.model,
@@ -4375,12 +4388,13 @@ var QwenProvider = class {
4375
4388
  role: "system",
4376
4389
  content: buildSystemContent(basePrompt, context)
4377
4390
  };
4391
+ const safeStreamMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4392
+ role: m.role || "user",
4393
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4394
+ }));
4378
4395
  const formattedMessages = [
4379
4396
  systemMessage,
4380
- ...messages.map((m) => ({
4381
- role: m.role,
4382
- content: m.content
4383
- }))
4397
+ ...safeStreamMessages
4384
4398
  ];
4385
4399
  const stream = yield new __await(this.client.chat.completions.create({
4386
4400
  model: this.llmConfig.model,
@@ -4523,12 +4537,16 @@ var UniversalLLMAdapter = class {
4523
4537
  async chat(messages, context) {
4524
4538
  var _a2, _b, _c;
4525
4539
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
4540
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4541
+ role: m.role || "user",
4542
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4543
+ }));
4526
4544
  const formattedMessages = [
4527
4545
  { role: "system", content: `${this.systemPrompt}
4528
4546
 
4529
4547
  Context:
4530
4548
  ${context != null ? context : "None"}` },
4531
- ...messages
4549
+ ...safeMessages
4532
4550
  ];
4533
4551
  let payload;
4534
4552
  if (this.opts.chatPayloadTemplate) {
@@ -4600,12 +4618,16 @@ ${context != null ? context : "None"}` },
4600
4618
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
4601
4619
  const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
4602
4620
  const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
4621
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4622
+ role: m.role || "user",
4623
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4624
+ }));
4603
4625
  const formattedMessages = [
4604
4626
  { role: "system", content: `${this.systemPrompt}
4605
4627
 
4606
4628
  Context:
4607
4629
  ${context != null ? context : "None"}` },
4608
- ...messages
4630
+ ...safeMessages
4609
4631
  ];
4610
4632
  let payload;
4611
4633
  if (this.opts.chatPayloadTemplate) {
@@ -6743,8 +6765,8 @@ var IntentClassifier = class {
6743
6765
  }
6744
6766
  // ─── Intent Heuristic Checkers ─────────────────────────────────────────────
6745
6767
  static isInformationLookup(query) {
6746
- 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)) {
6747
- if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top selling|best)\b/i.test(query)) {
6768
+ 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)) {
6769
+ if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top 10|top selling|best|chart|graph|plot|breakdown|analytics)\b/i.test(query)) {
6748
6770
  return true;
6749
6771
  }
6750
6772
  }
@@ -6781,7 +6803,7 @@ var Rule1SpecificInfoRule = class {
6781
6803
  }
6782
6804
  evaluate(context, intent) {
6783
6805
  const q = (context.userQuery || "").toLowerCase().trim();
6784
- 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);
6806
+ 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);
6785
6807
  const isNonVisual = !/\b(compare|versus|vs\.?|trend|monthly|revenue|best|top 10|chart|table|list|grid)\b/i.test(q);
6786
6808
  if (isFactQuery && isNonVisual) {
6787
6809
  return {
@@ -8060,6 +8082,11 @@ ${schemaProfileText}` : ""}`;
8060
8082
  }
8061
8083
  static chooseAutomaticVisualization(data, profile, query) {
8062
8084
  if (profile.records.length === 0) return null;
8085
+ const q = query.toLowerCase().trim();
8086
+ 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);
8087
+ if (!isExplicitVisualOrAnalytic) {
8088
+ return null;
8089
+ }
8063
8090
  if (profile.dateFields.length > 0 && profile.numericFields.length > 0) {
8064
8091
  return this.transformToLineChart(profile);
8065
8092
  }
@@ -8175,6 +8202,7 @@ ${schemaProfileText}` : ""}`;
8175
8202
  return Array.from(categories);
8176
8203
  }
8177
8204
  static getProductCategory(item) {
8205
+ if (!item) return null;
8178
8206
  const meta = item.metadata || {};
8179
8207
  const metadataCategory = resolveMetadataValue(meta, "category");
8180
8208
  if (this.isUsableCategory(metadataCategory)) return String(metadataCategory).trim();
@@ -8291,6 +8319,7 @@ ${schemaProfileText}` : ""}`;
8291
8319
  if (!fields.has(normalized)) fields.set(normalized, clean);
8292
8320
  };
8293
8321
  data.forEach((item) => {
8322
+ if (!item) return;
8294
8323
  Object.keys(item.metadata || {}).forEach(addField);
8295
8324
  for (const match of (item.content || "").matchAll(/(?:^|\n)\s*([A-Za-z][A-Za-z0-9_ /-]{1,50})\s*[:\-–]\s*([^\n]+)/g)) {
8296
8325
  addField(match[1]);
@@ -8407,6 +8436,7 @@ ${schemaProfileText}` : ""}`;
8407
8436
  return true;
8408
8437
  }
8409
8438
  static extractStockQuantity(item) {
8439
+ if (!item) return null;
8410
8440
  const meta = item.metadata || {};
8411
8441
  const stockValue = resolveMetadataValue(meta, "stock");
8412
8442
  const numericStock = this.toFiniteNumber(stockValue);
@@ -9667,7 +9697,10 @@ ${context}`;
9667
9697
  Focus on extracting the core intent and entities. Do not answer the question, just rewrite it.
9668
9698
 
9669
9699
  History:
9670
- ${history.map((m) => `${m.role}: ${m.content}`).join("\n")}
9700
+ ${(history || []).map((m) => {
9701
+ var _a2;
9702
+ return `${(m == null ? void 0 : m.role) || "user"}: ${(_a2 = m == null ? void 0 : m.content) != null ? _a2 : ""}`;
9703
+ }).join("\n")}
9671
9704
 
9672
9705
  New Question: ${question}
9673
9706
 
@@ -9691,8 +9724,11 @@ Optimized Search Query:`;
9691
9724
  const ns = formatNamespace(namespace != null ? namespace : this.config.projectId);
9692
9725
  try {
9693
9726
  const { sources } = await this.retrieve(query, { namespace: ns, topK: 5 });
9694
- if (sources.length === 0) return [];
9695
- const context = sources.map((s) => s.content).join("\n\n---\n\n");
9727
+ if (!sources || sources.length === 0) return [];
9728
+ const context = sources.map((s) => {
9729
+ var _a2;
9730
+ return (_a2 = s == null ? void 0 : s.content) != null ? _a2 : "";
9731
+ }).join("\n\n---\n\n");
9696
9732
  const prompt = `Based on the following snippets from a document, what are at least 5 short, relevant questions a user might ask?
9697
9733
  Focus on questions that can be answered by the context.
9698
9734
  Keep each question under 10 words and make them very specific to the content.
@@ -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,
@@ -4488,12 +4502,16 @@ var UniversalLLMAdapter = class {
4488
4502
  async chat(messages, context) {
4489
4503
  var _a2, _b, _c;
4490
4504
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
4505
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4506
+ role: m.role || "user",
4507
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4508
+ }));
4491
4509
  const formattedMessages = [
4492
4510
  { role: "system", content: `${this.systemPrompt}
4493
4511
 
4494
4512
  Context:
4495
4513
  ${context != null ? context : "None"}` },
4496
- ...messages
4514
+ ...safeMessages
4497
4515
  ];
4498
4516
  let payload;
4499
4517
  if (this.opts.chatPayloadTemplate) {
@@ -4565,12 +4583,16 @@ ${context != null ? context : "None"}` },
4565
4583
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
4566
4584
  const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
4567
4585
  const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
4586
+ const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
4587
+ role: m.role || "user",
4588
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
4589
+ }));
4568
4590
  const formattedMessages = [
4569
4591
  { role: "system", content: `${this.systemPrompt}
4570
4592
 
4571
4593
  Context:
4572
4594
  ${context != null ? context : "None"}` },
4573
- ...messages
4595
+ ...safeMessages
4574
4596
  ];
4575
4597
  let payload;
4576
4598
  if (this.opts.chatPayloadTemplate) {
@@ -6708,8 +6730,8 @@ var IntentClassifier = class {
6708
6730
  }
6709
6731
  // ─── Intent Heuristic Checkers ─────────────────────────────────────────────
6710
6732
  static isInformationLookup(query) {
6711
- 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)) {
6712
- if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top selling|best)\b/i.test(query)) {
6733
+ 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)) {
6734
+ if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top 10|top selling|best|chart|graph|plot|breakdown|analytics)\b/i.test(query)) {
6713
6735
  return true;
6714
6736
  }
6715
6737
  }
@@ -6746,7 +6768,7 @@ var Rule1SpecificInfoRule = class {
6746
6768
  }
6747
6769
  evaluate(context, intent) {
6748
6770
  const q = (context.userQuery || "").toLowerCase().trim();
6749
- 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);
6771
+ 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);
6750
6772
  const isNonVisual = !/\b(compare|versus|vs\.?|trend|monthly|revenue|best|top 10|chart|table|list|grid)\b/i.test(q);
6751
6773
  if (isFactQuery && isNonVisual) {
6752
6774
  return {
@@ -8025,6 +8047,11 @@ ${schemaProfileText}` : ""}`;
8025
8047
  }
8026
8048
  static chooseAutomaticVisualization(data, profile, query) {
8027
8049
  if (profile.records.length === 0) return null;
8050
+ const q = query.toLowerCase().trim();
8051
+ 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);
8052
+ if (!isExplicitVisualOrAnalytic) {
8053
+ return null;
8054
+ }
8028
8055
  if (profile.dateFields.length > 0 && profile.numericFields.length > 0) {
8029
8056
  return this.transformToLineChart(profile);
8030
8057
  }
@@ -8140,6 +8167,7 @@ ${schemaProfileText}` : ""}`;
8140
8167
  return Array.from(categories);
8141
8168
  }
8142
8169
  static getProductCategory(item) {
8170
+ if (!item) return null;
8143
8171
  const meta = item.metadata || {};
8144
8172
  const metadataCategory = resolveMetadataValue(meta, "category");
8145
8173
  if (this.isUsableCategory(metadataCategory)) return String(metadataCategory).trim();
@@ -8256,6 +8284,7 @@ ${schemaProfileText}` : ""}`;
8256
8284
  if (!fields.has(normalized)) fields.set(normalized, clean);
8257
8285
  };
8258
8286
  data.forEach((item) => {
8287
+ if (!item) return;
8259
8288
  Object.keys(item.metadata || {}).forEach(addField);
8260
8289
  for (const match of (item.content || "").matchAll(/(?:^|\n)\s*([A-Za-z][A-Za-z0-9_ /-]{1,50})\s*[:\-–]\s*([^\n]+)/g)) {
8261
8290
  addField(match[1]);
@@ -8372,6 +8401,7 @@ ${schemaProfileText}` : ""}`;
8372
8401
  return true;
8373
8402
  }
8374
8403
  static extractStockQuantity(item) {
8404
+ if (!item) return null;
8375
8405
  const meta = item.metadata || {};
8376
8406
  const stockValue = resolveMetadataValue(meta, "stock");
8377
8407
  const numericStock = this.toFiniteNumber(stockValue);
@@ -9632,7 +9662,10 @@ ${context}`;
9632
9662
  Focus on extracting the core intent and entities. Do not answer the question, just rewrite it.
9633
9663
 
9634
9664
  History:
9635
- ${history.map((m) => `${m.role}: ${m.content}`).join("\n")}
9665
+ ${(history || []).map((m) => {
9666
+ var _a2;
9667
+ return `${(m == null ? void 0 : m.role) || "user"}: ${(_a2 = m == null ? void 0 : m.content) != null ? _a2 : ""}`;
9668
+ }).join("\n")}
9636
9669
 
9637
9670
  New Question: ${question}
9638
9671
 
@@ -9656,8 +9689,11 @@ Optimized Search Query:`;
9656
9689
  const ns = formatNamespace(namespace != null ? namespace : this.config.projectId);
9657
9690
  try {
9658
9691
  const { sources } = await this.retrieve(query, { namespace: ns, topK: 5 });
9659
- if (sources.length === 0) return [];
9660
- const context = sources.map((s) => s.content).join("\n\n---\n\n");
9692
+ if (!sources || sources.length === 0) return [];
9693
+ const context = sources.map((s) => {
9694
+ var _a2;
9695
+ return (_a2 = s == null ? void 0 : s.content) != null ? _a2 : "";
9696
+ }).join("\n\n---\n\n");
9661
9697
  const prompt = `Based on the following snippets from a document, what are at least 5 short, relevant questions a user might ask?
9662
9698
  Focus on questions that can be answered by the context.
9663
9699
  Keep each question under 10 words and make them very specific to the content.
package/dist/index.js CHANGED
@@ -1739,32 +1739,31 @@ function isLikelyUiOnlyMessage(text) {
1739
1739
  }
1740
1740
  function extractProductsFromSources(sources, isUser) {
1741
1741
  if (isUser || !sources) return [];
1742
- return sources.filter((s) => {
1743
- var _a;
1742
+ return sources.filter((s) => Boolean(s && typeof s === "object")).filter((s) => {
1743
+ var _a, _b;
1744
1744
  const m = (_a = s.metadata) != null ? _a : {};
1745
1745
  const keys = Object.keys(m).map((k) => k.toLowerCase());
1746
1746
  const hasStrongProductKey = keys.some(
1747
1747
  (k) => ["price", "image", "img", "thumbnail", "images", "product", "sku", "cost"].includes(k)
1748
1748
  );
1749
1749
  const hasProductIdentity = keys.some((k) => ["brand", "model"].includes(k)) && keys.some((k) => ["name", "title", "product_name", "product"].includes(k));
1750
- const hasPricePattern = /\$\s*\d+/.test(s.content);
1750
+ const hasPricePattern = /\$\s*\d+/.test((_b = s == null ? void 0 : s.content) != null ? _b : "");
1751
1751
  return hasStrongProductKey || hasProductIdentity || hasPricePattern || m.type === "product";
1752
1752
  }).map((s) => {
1753
- var _a, _b, _c;
1753
+ var _a, _b, _c, _d;
1754
1754
  const m = (_a = s.metadata) != null ? _a : {};
1755
1755
  const name = resolveMetadataValue(m, "name");
1756
1756
  const brand = resolveMetadataValue(m, "brand");
1757
1757
  const price = resolveMetadataValue(m, "price");
1758
1758
  const description = resolveMetadataValue(m, "description");
1759
1759
  return {
1760
- id: s.id,
1761
- name: (_c = name != null ? name : ((_b = s == null ? void 0 : s.content) != null ? _b : "").split("\n")[0]) != null ? _c : "Unknown Product",
1760
+ id: (_b = s == null ? void 0 : s.id) != null ? _b : `prod_${Math.random().toString(36).substring(2, 8)}`,
1761
+ name: (_d = name != null ? name : ((_c = s == null ? void 0 : s.content) != null ? _c : "").split("\n")[0]) != null ? _d : "Unknown Product",
1762
1762
  brand,
1763
1763
  price,
1764
1764
  image: resolveImage(m),
1765
1765
  link: resolveMetadataValue(m, "link"),
1766
1766
  description: description ? description : ""
1767
- //description ?? s.content,
1768
1767
  };
1769
1768
  });
1770
1769
  }
@@ -3142,10 +3141,10 @@ function ChatWindow({
3142
3141
  ] })
3143
3142
  ] }),
3144
3143
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1.5", children: [
3145
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center gap-1 text-[10px] text-emerald-500 dark:text-emerald-400 mr-2", children: [
3144
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex items-center gap-1.5 mr-2", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center gap-1 text-[10px] text-emerald-500 dark:text-emerald-400", children: [
3146
3145
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-500 dark:bg-emerald-400 animate-pulse" }),
3147
3146
  "Online"
3148
- ] }),
3147
+ ] }) }),
3149
3148
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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: [
3150
3149
  mounted && messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3151
3150
  "button",
@@ -3673,8 +3672,8 @@ var IntentClassifier = class {
3673
3672
  }
3674
3673
  // ─── Intent Heuristic Checkers ─────────────────────────────────────────────
3675
3674
  static isInformationLookup(query) {
3676
- 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)) {
3677
- if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top selling|best)\b/i.test(query)) {
3675
+ 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)) {
3676
+ if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top 10|top selling|best|chart|graph|plot|breakdown|analytics)\b/i.test(query)) {
3678
3677
  return true;
3679
3678
  }
3680
3679
  }
@@ -3711,7 +3710,7 @@ var Rule1SpecificInfoRule = class {
3711
3710
  }
3712
3711
  evaluate(context, intent) {
3713
3712
  const q = (context.userQuery || "").toLowerCase().trim();
3714
- 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);
3713
+ 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);
3715
3714
  const isNonVisual = !/\b(compare|versus|vs\.?|trend|monthly|revenue|best|top 10|chart|table|list|grid)\b/i.test(q);
3716
3715
  if (isFactQuery && isNonVisual) {
3717
3716
  return {
@@ -4849,6 +4848,11 @@ ${schemaProfileText}` : ""}`;
4849
4848
  }
4850
4849
  static chooseAutomaticVisualization(data, profile, query) {
4851
4850
  if (profile.records.length === 0) return null;
4851
+ const q = query.toLowerCase().trim();
4852
+ 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);
4853
+ if (!isExplicitVisualOrAnalytic) {
4854
+ return null;
4855
+ }
4852
4856
  if (profile.dateFields.length > 0 && profile.numericFields.length > 0) {
4853
4857
  return this.transformToLineChart(profile);
4854
4858
  }
@@ -4964,6 +4968,7 @@ ${schemaProfileText}` : ""}`;
4964
4968
  return Array.from(categories);
4965
4969
  }
4966
4970
  static getProductCategory(item) {
4971
+ if (!item) return null;
4967
4972
  const meta = item.metadata || {};
4968
4973
  const metadataCategory = resolveMetadataValue(meta, "category");
4969
4974
  if (this.isUsableCategory(metadataCategory)) return String(metadataCategory).trim();
@@ -5080,6 +5085,7 @@ ${schemaProfileText}` : ""}`;
5080
5085
  if (!fields.has(normalized)) fields.set(normalized, clean);
5081
5086
  };
5082
5087
  data.forEach((item) => {
5088
+ if (!item) return;
5083
5089
  Object.keys(item.metadata || {}).forEach(addField);
5084
5090
  for (const match of (item.content || "").matchAll(/(?:^|\n)\s*([A-Za-z][A-Za-z0-9_ /-]{1,50})\s*[:\-–]\s*([^\n]+)/g)) {
5085
5091
  addField(match[1]);
@@ -5196,6 +5202,7 @@ ${schemaProfileText}` : ""}`;
5196
5202
  return true;
5197
5203
  }
5198
5204
  static extractStockQuantity(item) {
5205
+ if (!item) return null;
5199
5206
  const meta = item.metadata || {};
5200
5207
  const stockValue = resolveMetadataValue(meta, "stock");
5201
5208
  const numericStock = this.toFiniteNumber(stockValue);