@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/handlers/index.js +113 -75
- package/dist/handlers/index.mjs +113 -75
- package/dist/index.js +19 -12
- package/dist/index.mjs +19 -12
- package/dist/server.js +113 -75
- package/dist/server.mjs +113 -75
- package/package.json +1 -1
- package/src/components/ChatWindow.tsx +22 -22
- package/src/core/Pipeline.ts +3 -3
- package/src/llm/providers/GeminiProvider.ts +4 -4
- package/src/llm/providers/GroqProvider.ts +16 -8
- package/src/llm/providers/OpenAIProvider.ts +16 -8
- package/src/llm/providers/QwenProvider.ts +16 -8
- package/src/llm/providers/UniversalLLMAdapter.ts +56 -36
- package/src/rendering/IntentClassifier.ts +3 -3
- package/src/rendering/rules/Rule1SpecificInfoRule.ts +1 -1
- package/src/utils/ProductExtractor.ts +4 -3
- package/src/utils/UITransformer.ts +12 -0
package/dist/handlers/index.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 (
|
|
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 (
|
|
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 (
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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,
|
|
@@ -4486,14 +4500,18 @@ var UniversalLLMAdapter = class {
|
|
|
4486
4500
|
});
|
|
4487
4501
|
}
|
|
4488
4502
|
async chat(messages, context) {
|
|
4489
|
-
var _a2, _b, _c;
|
|
4503
|
+
var _a2, _b, _c, _d, _e;
|
|
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
|
-
...
|
|
4514
|
+
...safeMessages
|
|
4497
4515
|
];
|
|
4498
4516
|
let payload;
|
|
4499
4517
|
if (this.opts.chatPayloadTemplate) {
|
|
@@ -4511,8 +4529,15 @@ ${context != null ? context : "None"}` },
|
|
|
4511
4529
|
temperature: this.temperature
|
|
4512
4530
|
};
|
|
4513
4531
|
}
|
|
4514
|
-
|
|
4515
|
-
|
|
4532
|
+
try {
|
|
4533
|
+
const { data: data2 } = await this.http.post(path2, payload);
|
|
4534
|
+
const extractPath2 = (_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content";
|
|
4535
|
+
const result2 = (_c = resolvePath(data2, extractPath2)) != null ? _c : extractContent(data2);
|
|
4536
|
+
if (result2 !== void 0) {
|
|
4537
|
+
return String(result2);
|
|
4538
|
+
}
|
|
4539
|
+
} catch (httpErr) {
|
|
4540
|
+
console.warn(`[UniversalLLMAdapter] Direct HTTP POST to ${this.baseUrl}${path2} failed (${httpErr.message}). Attempting in-process gateway dispatch fallback...`);
|
|
4516
4541
|
const _g2 = globalThis;
|
|
4517
4542
|
let dispatch = _g2.__retrivoraDispatchChat;
|
|
4518
4543
|
if (typeof dispatch !== "function") {
|
|
@@ -4527,28 +4552,22 @@ ${context != null ? context : "None"}` },
|
|
|
4527
4552
|
}
|
|
4528
4553
|
}
|
|
4529
4554
|
if (typeof dispatch === "function") {
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
return content;
|
|
4540
|
-
}
|
|
4541
|
-
throw new Error(`[UniversalLLMAdapter] In-process dispatch returned empty content for model: ${this.model}`);
|
|
4542
|
-
} catch (dispatchErr) {
|
|
4543
|
-
throw dispatchErr;
|
|
4555
|
+
const res = await dispatch({
|
|
4556
|
+
model: this.model,
|
|
4557
|
+
messages: formattedMessages,
|
|
4558
|
+
max_tokens: this.maxTokens,
|
|
4559
|
+
temperature: this.temperature
|
|
4560
|
+
}, this.apiKey);
|
|
4561
|
+
const content = extractContent(res == null ? void 0 : res.response);
|
|
4562
|
+
if (content !== void 0) {
|
|
4563
|
+
return content;
|
|
4544
4564
|
}
|
|
4545
|
-
} else {
|
|
4546
|
-
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.`);
|
|
4547
4565
|
}
|
|
4566
|
+
throw httpErr;
|
|
4548
4567
|
}
|
|
4549
4568
|
const { data } = await this.http.post(path2, payload);
|
|
4550
|
-
const extractPath = (
|
|
4551
|
-
const result = (
|
|
4569
|
+
const extractPath = (_d = this.opts.responseExtractPath) != null ? _d : "choices[0].message.content";
|
|
4570
|
+
const result = (_e = resolvePath(data, extractPath)) != null ? _e : extractContent(data);
|
|
4552
4571
|
if (result === void 0) {
|
|
4553
4572
|
throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
|
|
4554
4573
|
}
|
|
@@ -4565,12 +4584,16 @@ ${context != null ? context : "None"}` },
|
|
|
4565
4584
|
const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
|
|
4566
4585
|
const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
|
|
4567
4586
|
const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
|
|
4587
|
+
const safeMessages = (Array.isArray(messages) ? messages : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
|
|
4588
|
+
role: m.role || "user",
|
|
4589
|
+
content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
|
|
4590
|
+
}));
|
|
4568
4591
|
const formattedMessages = [
|
|
4569
4592
|
{ role: "system", content: `${this.systemPrompt}
|
|
4570
4593
|
|
|
4571
4594
|
Context:
|
|
4572
4595
|
${context != null ? context : "None"}` },
|
|
4573
|
-
...
|
|
4596
|
+
...safeMessages
|
|
4574
4597
|
];
|
|
4575
4598
|
let payload;
|
|
4576
4599
|
if (this.opts.chatPayloadTemplate) {
|
|
@@ -4686,7 +4709,7 @@ ${context != null ? context : "None"}` },
|
|
|
4686
4709
|
});
|
|
4687
4710
|
}
|
|
4688
4711
|
async embed(text) {
|
|
4689
|
-
var _a2, _b, _c, _d;
|
|
4712
|
+
var _a2, _b, _c, _d, _e;
|
|
4690
4713
|
const path2 = (_a2 = this.opts.embedPath) != null ? _a2 : "/embeddings";
|
|
4691
4714
|
let payload;
|
|
4692
4715
|
if (this.opts.embedPayloadTemplate) {
|
|
@@ -4700,8 +4723,15 @@ ${context != null ? context : "None"}` },
|
|
|
4700
4723
|
input: text
|
|
4701
4724
|
};
|
|
4702
4725
|
}
|
|
4703
|
-
|
|
4704
|
-
|
|
4726
|
+
try {
|
|
4727
|
+
const { data: data2 } = await this.http.post(path2, payload);
|
|
4728
|
+
const extractPath2 = (_b = this.opts.embedExtractPath) != null ? _b : "data[0].embedding";
|
|
4729
|
+
const vector2 = resolvePath(data2, extractPath2);
|
|
4730
|
+
if (Array.isArray(vector2)) {
|
|
4731
|
+
return vector2;
|
|
4732
|
+
}
|
|
4733
|
+
} catch (httpErr) {
|
|
4734
|
+
console.warn(`[UniversalLLMAdapter] Direct HTTP embedding POST to ${this.baseUrl}${path2} failed (${httpErr.message}). Attempting in-process gateway dispatch fallback...`);
|
|
4705
4735
|
const _g2 = globalThis;
|
|
4706
4736
|
let dispatch = _g2.__retrivoraDispatchEmbedding;
|
|
4707
4737
|
if (typeof dispatch !== "function") {
|
|
@@ -4716,24 +4746,18 @@ ${context != null ? context : "None"}` },
|
|
|
4716
4746
|
}
|
|
4717
4747
|
}
|
|
4718
4748
|
if (typeof dispatch === "function") {
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
return res.data[0].embedding;
|
|
4726
|
-
}
|
|
4727
|
-
throw new Error(`[UniversalLLMAdapter] In-process embedding dispatch returned invalid vector for model: ${this.model}`);
|
|
4728
|
-
} catch (dispatchErr) {
|
|
4729
|
-
throw dispatchErr;
|
|
4749
|
+
const res = await dispatch({
|
|
4750
|
+
model: this.model,
|
|
4751
|
+
input: text
|
|
4752
|
+
}, this.apiKey);
|
|
4753
|
+
if ((_d = (_c = res == null ? void 0 : res.data) == null ? void 0 : _c[0]) == null ? void 0 : _d.embedding) {
|
|
4754
|
+
return res.data[0].embedding;
|
|
4730
4755
|
}
|
|
4731
|
-
} else {
|
|
4732
|
-
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.`);
|
|
4733
4756
|
}
|
|
4757
|
+
throw httpErr;
|
|
4734
4758
|
}
|
|
4735
4759
|
const { data } = await this.http.post(path2, payload);
|
|
4736
|
-
const extractPath = (
|
|
4760
|
+
const extractPath = (_e = this.opts.embedExtractPath) != null ? _e : "data[0].embedding";
|
|
4737
4761
|
const vector = resolvePath(data, extractPath);
|
|
4738
4762
|
if (!Array.isArray(vector)) {
|
|
4739
4763
|
throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
|
|
@@ -6708,8 +6732,8 @@ var IntentClassifier = class {
|
|
|
6708
6732
|
}
|
|
6709
6733
|
// ─── Intent Heuristic Checkers ─────────────────────────────────────────────
|
|
6710
6734
|
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)) {
|
|
6735
|
+
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)) {
|
|
6736
|
+
if (!/\b(compare|versus|vs\.?|trend|monthly|revenue|top 10|top selling|best|chart|graph|plot|breakdown|analytics)\b/i.test(query)) {
|
|
6713
6737
|
return true;
|
|
6714
6738
|
}
|
|
6715
6739
|
}
|
|
@@ -6746,7 +6770,7 @@ var Rule1SpecificInfoRule = class {
|
|
|
6746
6770
|
}
|
|
6747
6771
|
evaluate(context, intent) {
|
|
6748
6772
|
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);
|
|
6773
|
+
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
6774
|
const isNonVisual = !/\b(compare|versus|vs\.?|trend|monthly|revenue|best|top 10|chart|table|list|grid)\b/i.test(q);
|
|
6751
6775
|
if (isFactQuery && isNonVisual) {
|
|
6752
6776
|
return {
|
|
@@ -8025,6 +8049,11 @@ ${schemaProfileText}` : ""}`;
|
|
|
8025
8049
|
}
|
|
8026
8050
|
static chooseAutomaticVisualization(data, profile, query) {
|
|
8027
8051
|
if (profile.records.length === 0) return null;
|
|
8052
|
+
const q = query.toLowerCase().trim();
|
|
8053
|
+
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);
|
|
8054
|
+
if (!isExplicitVisualOrAnalytic) {
|
|
8055
|
+
return null;
|
|
8056
|
+
}
|
|
8028
8057
|
if (profile.dateFields.length > 0 && profile.numericFields.length > 0) {
|
|
8029
8058
|
return this.transformToLineChart(profile);
|
|
8030
8059
|
}
|
|
@@ -8140,6 +8169,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
8140
8169
|
return Array.from(categories);
|
|
8141
8170
|
}
|
|
8142
8171
|
static getProductCategory(item) {
|
|
8172
|
+
if (!item) return null;
|
|
8143
8173
|
const meta = item.metadata || {};
|
|
8144
8174
|
const metadataCategory = resolveMetadataValue(meta, "category");
|
|
8145
8175
|
if (this.isUsableCategory(metadataCategory)) return String(metadataCategory).trim();
|
|
@@ -8256,6 +8286,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
8256
8286
|
if (!fields.has(normalized)) fields.set(normalized, clean);
|
|
8257
8287
|
};
|
|
8258
8288
|
data.forEach((item) => {
|
|
8289
|
+
if (!item) return;
|
|
8259
8290
|
Object.keys(item.metadata || {}).forEach(addField);
|
|
8260
8291
|
for (const match of (item.content || "").matchAll(/(?:^|\n)\s*([A-Za-z][A-Za-z0-9_ /-]{1,50})\s*[:\-–]\s*([^\n]+)/g)) {
|
|
8261
8292
|
addField(match[1]);
|
|
@@ -8372,6 +8403,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
8372
8403
|
return true;
|
|
8373
8404
|
}
|
|
8374
8405
|
static extractStockQuantity(item) {
|
|
8406
|
+
if (!item) return null;
|
|
8375
8407
|
const meta = item.metadata || {};
|
|
8376
8408
|
const stockValue = resolveMetadataValue(meta, "stock");
|
|
8377
8409
|
const numericStock = this.toFiniteNumber(stockValue);
|
|
@@ -9632,7 +9664,10 @@ ${context}`;
|
|
|
9632
9664
|
Focus on extracting the core intent and entities. Do not answer the question, just rewrite it.
|
|
9633
9665
|
|
|
9634
9666
|
History:
|
|
9635
|
-
${history.map((m) =>
|
|
9667
|
+
${(history || []).map((m) => {
|
|
9668
|
+
var _a2;
|
|
9669
|
+
return `${(m == null ? void 0 : m.role) || "user"}: ${(_a2 = m == null ? void 0 : m.content) != null ? _a2 : ""}`;
|
|
9670
|
+
}).join("\n")}
|
|
9636
9671
|
|
|
9637
9672
|
New Question: ${question}
|
|
9638
9673
|
|
|
@@ -9656,8 +9691,11 @@ Optimized Search Query:`;
|
|
|
9656
9691
|
const ns = formatNamespace(namespace != null ? namespace : this.config.projectId);
|
|
9657
9692
|
try {
|
|
9658
9693
|
const { sources } = await this.retrieve(query, { namespace: ns, topK: 5 });
|
|
9659
|
-
if (sources.length === 0) return [];
|
|
9660
|
-
const context = sources.map((s) =>
|
|
9694
|
+
if (!sources || sources.length === 0) return [];
|
|
9695
|
+
const context = sources.map((s) => {
|
|
9696
|
+
var _a2;
|
|
9697
|
+
return (_a2 = s == null ? void 0 : s.content) != null ? _a2 : "";
|
|
9698
|
+
}).join("\n\n---\n\n");
|
|
9661
9699
|
const prompt = `Based on the following snippets from a document, what are at least 5 short, relevant questions a user might ask?
|
|
9662
9700
|
Focus on questions that can be answered by the context.
|
|
9663
9701
|
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: (
|
|
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
|
|
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);
|
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: (
|
|
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
|
|
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);
|