@retrivora-ai/rag-engine 1.2.0 → 1.2.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.
Files changed (49) hide show
  1. package/dist/DocumentChunker-BXOUMKoP.d.ts +93 -0
  2. package/dist/DocumentChunker-D1dg5iCi.d.mts +93 -0
  3. package/dist/{chunk-OCDCJUNE.mjs → chunk-GCPPRD2G.mjs} +70 -1
  4. package/dist/handlers/index.d.mts +2 -2
  5. package/dist/handlers/index.d.ts +2 -2
  6. package/dist/handlers/index.js +70 -0
  7. package/dist/handlers/index.mjs +3 -1
  8. package/dist/{index-CYgr00ot.d.ts → index-B67KQ9NN.d.ts} +14 -2
  9. package/dist/{RagConfig-FyMB_UG6.d.mts → index-Cti1u0y1.d.mts} +87 -86
  10. package/dist/{RagConfig-FyMB_UG6.d.ts → index-Cti1u0y1.d.ts} +87 -86
  11. package/dist/{index-D-lfcqlL.d.mts → index-kUXnRvuI.d.mts} +14 -2
  12. package/dist/index.d.mts +53 -89
  13. package/dist/index.d.ts +53 -89
  14. package/dist/index.js +139 -27
  15. package/dist/index.mjs +143 -27
  16. package/dist/server.d.mts +9 -4
  17. package/dist/server.d.ts +9 -4
  18. package/dist/server.js +51 -0
  19. package/dist/server.mjs +1 -1
  20. package/package.json +1 -1
  21. package/src/app/api/suggestions/route.ts +4 -0
  22. package/src/components/ChatWidget.tsx +1 -7
  23. package/src/components/ChatWindow.tsx +180 -25
  24. package/src/components/ConfigProvider.tsx +7 -33
  25. package/src/components/DocumentUpload.tsx +1 -8
  26. package/src/components/MessageBubble.tsx +1 -12
  27. package/src/components/ProductCard.tsx +1 -15
  28. package/src/components/ProductCarousel.tsx +2 -7
  29. package/src/components/SourceCard.tsx +1 -6
  30. package/src/config/RagConfig.ts +2 -0
  31. package/src/config/uiConstants.ts +23 -0
  32. package/src/core/Pipeline.ts +57 -1
  33. package/src/core/VectorPlugin.ts +8 -1
  34. package/src/handlers/index.ts +31 -1
  35. package/src/hooks/useRagChat.ts +1 -45
  36. package/src/hooks/useStoredMessages.ts +1 -1
  37. package/src/index.ts +20 -5
  38. package/src/llm/ILLMProvider.ts +1 -13
  39. package/src/llm/providers/AnthropicProvider.ts +2 -1
  40. package/src/llm/providers/GeminiProvider.ts +2 -1
  41. package/src/llm/providers/OllamaProvider.ts +2 -1
  42. package/src/llm/providers/OpenAIProvider.ts +2 -1
  43. package/src/llm/providers/UniversalLLMAdapter.ts +2 -1
  44. package/src/server.ts +2 -2
  45. package/src/types/chat.ts +53 -0
  46. package/src/types/index.ts +17 -0
  47. package/src/types/props.ts +79 -0
  48. package/dist/DocumentChunker-Dh9TvmGG.d.mts +0 -45
  49. package/dist/DocumentChunker-Dh9TvmGG.d.ts +0 -45
package/dist/index.js CHANGED
@@ -338,8 +338,8 @@ function mergeDefined(base, override) {
338
338
  return merged;
339
339
  }
340
340
 
341
- // src/components/ConfigProvider.tsx
342
- var defaultConfig = {
341
+ // src/config/uiConstants.ts
342
+ var DEFAULT_CONFIG = {
343
343
  projectId: "default",
344
344
  ui: {
345
345
  title: "AI Assistant",
@@ -355,17 +355,33 @@ var defaultConfig = {
355
355
  visualStyle: "glass",
356
356
  borderRadius: "xl",
357
357
  allowUpload: true,
358
- allowResize: true
358
+ allowResize: true,
359
+ enableVoiceInput: true
359
360
  }
360
361
  };
361
- var ConfigContext = (0, import_react5.createContext)(defaultConfig);
362
+ var BORDER_RADIUS_MAP = {
363
+ none: "rounded-none",
364
+ sm: "rounded-sm",
365
+ md: "rounded-md",
366
+ lg: "rounded-lg",
367
+ xl: "rounded-xl",
368
+ full: "rounded-3xl"
369
+ };
370
+ var CHAT_SUGGESTIONS = [
371
+ "What can you help me with?",
372
+ "Summarise the key topics",
373
+ "Show me an example"
374
+ ];
375
+
376
+ // src/components/ConfigProvider.tsx
377
+ var ConfigContext = (0, import_react5.createContext)(DEFAULT_CONFIG);
362
378
  function ConfigProvider({
363
379
  config,
364
380
  children
365
381
  }) {
366
382
  const merged = {
367
- projectId: (config == null ? void 0 : config.projectId) || defaultConfig.projectId,
368
- ui: mergeDefined(defaultConfig.ui, config == null ? void 0 : config.ui)
383
+ projectId: (config == null ? void 0 : config.projectId) || DEFAULT_CONFIG.projectId,
384
+ ui: mergeDefined(DEFAULT_CONFIG.ui, config == null ? void 0 : config.ui)
369
385
  };
370
386
  return /* @__PURE__ */ import_react5.default.createElement(ConfigContext.Provider, { value: merged }, children);
371
387
  }
@@ -563,21 +579,6 @@ function useRagChat(projectId, options = {}) {
563
579
  };
564
580
  }
565
581
 
566
- // src/config/uiConstants.ts
567
- var BORDER_RADIUS_MAP = {
568
- none: "rounded-none",
569
- sm: "rounded-sm",
570
- md: "rounded-md",
571
- lg: "rounded-lg",
572
- xl: "rounded-xl",
573
- full: "rounded-3xl"
574
- };
575
- var CHAT_SUGGESTIONS = [
576
- "What can you help me with?",
577
- "Summarise the key topics",
578
- "Show me an example"
579
- ];
580
-
581
582
  // src/components/ChatWindow.tsx
582
583
  function ChatWindow({
583
584
  className = "",
@@ -600,6 +601,56 @@ function ChatWindow({
600
601
  const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
601
602
  namespace: projectId
602
603
  });
604
+ const [suggestions, setSuggestions] = (0, import_react7.useState)([]);
605
+ const [isSuggesting, setIsSuggesting] = (0, import_react7.useState)(false);
606
+ const [isListening, setIsListening] = (0, import_react7.useState)(false);
607
+ const recognitionRef = (0, import_react7.useRef)(null);
608
+ (0, import_react7.useEffect)(() => {
609
+ if (typeof window !== "undefined") {
610
+ const win = window;
611
+ const SpeechRecognition = win.SpeechRecognition || win.webkitSpeechRecognition;
612
+ if (SpeechRecognition) {
613
+ const recognition = new SpeechRecognition();
614
+ recognition.continuous = true;
615
+ recognition.interimResults = true;
616
+ recognition.onresult = (event) => {
617
+ let finalTranscript = "";
618
+ for (let i = event.resultIndex; i < event.results.length; ++i) {
619
+ if (event.results[i].isFinal) {
620
+ finalTranscript += event.results[i][0].transcript;
621
+ }
622
+ }
623
+ if (finalTranscript) {
624
+ setInput((prev) => (prev ? prev + " " : "") + finalTranscript.trim());
625
+ }
626
+ };
627
+ recognition.onerror = (event) => {
628
+ console.error("Speech recognition error:", event.error);
629
+ setIsListening(false);
630
+ };
631
+ recognition.onend = () => {
632
+ setIsListening(false);
633
+ };
634
+ recognitionRef.current = recognition;
635
+ }
636
+ }
637
+ }, []);
638
+ const toggleListening = () => {
639
+ if (!recognitionRef.current) {
640
+ alert("Speech recognition is not supported in your browser.");
641
+ return;
642
+ }
643
+ if (isListening) {
644
+ recognitionRef.current.stop();
645
+ } else {
646
+ try {
647
+ recognitionRef.current.start();
648
+ setIsListening(true);
649
+ } catch (err) {
650
+ console.error("Failed to start recognition:", err);
651
+ }
652
+ }
653
+ };
603
654
  (0, import_react7.useEffect)(() => {
604
655
  setMounted(true);
605
656
  }, []);
@@ -610,15 +661,21 @@ function ChatWindow({
610
661
  }
611
662
  }, [messages, isLoading]);
612
663
  const sendMessage = (0, import_react7.useCallback)(async () => {
664
+ var _a2;
613
665
  const text = input.trim();
614
666
  if (!text || isLoading) return;
667
+ if (isListening) {
668
+ (_a2 = recognitionRef.current) == null ? void 0 : _a2.stop();
669
+ }
615
670
  setInput("");
671
+ setSuggestions([]);
672
+ setIsSuggesting(false);
616
673
  await send(text);
617
674
  window.setTimeout(() => {
618
- var _a2;
619
- return (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
675
+ var _a3;
676
+ return (_a3 = inputRef.current) == null ? void 0 : _a3.focus();
620
677
  }, 50);
621
- }, [input, isLoading, send]);
678
+ }, [input, isLoading, send, isListening]);
622
679
  const handleKeyDown = (e) => {
623
680
  if (e.key === "Enter" && !e.shiftKey) {
624
681
  e.preventDefault();
@@ -627,10 +684,36 @@ function ChatWindow({
627
684
  };
628
685
  const clearHistory = () => {
629
686
  clear();
687
+ setSuggestions([]);
688
+ setIsSuggesting(false);
630
689
  };
631
690
  const isEmpty = messages.length === 0;
632
691
  const currentRadius = BORDER_RADIUS_MAP[ui.borderRadius || "xl"];
633
692
  const isGlass = ui.visualStyle !== "solid";
693
+ (0, import_react7.useEffect)(() => {
694
+ if (input.trim().length < 3) {
695
+ return;
696
+ }
697
+ const timer = setTimeout(async () => {
698
+ setIsSuggesting(true);
699
+ try {
700
+ const response = await fetch("/api/suggestions", {
701
+ method: "POST",
702
+ headers: { "Content-Type": "application/json" },
703
+ body: JSON.stringify({ query: input, namespace: projectId })
704
+ });
705
+ const data = await response.json();
706
+ if (data.suggestions) {
707
+ setSuggestions(data.suggestions);
708
+ }
709
+ } catch (err) {
710
+ console.error("Failed to fetch suggestions:", err);
711
+ } finally {
712
+ setIsSuggesting(false);
713
+ }
714
+ }, 800);
715
+ return () => clearTimeout(timer);
716
+ }, [input, projectId]);
634
717
  return /* @__PURE__ */ import_react7.default.createElement(
635
718
  "div",
636
719
  {
@@ -735,19 +818,39 @@ function ChatWindow({
735
818
  )), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */ import_react7.default.createElement(
736
819
  MessageBubble,
737
820
  {
738
- message: { role: "assistant", content: "" },
821
+ message: { id: "loading", role: "assistant", content: "", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
739
822
  isStreaming: true,
740
823
  primaryColor: ui.primaryColor,
741
824
  accentColor: ui.accentColor,
742
825
  onAddToCart
743
826
  }
744
827
  ), error && /* @__PURE__ */ import_react7.default.createElement("div", { className: "flex items-center gap-2 text-rose-500 dark:text-rose-400 text-sm bg-rose-50 dark:bg-rose-400/10 border border-rose-200 dark:border-rose-400/20 rounded-xl px-4 py-3" }, /* @__PURE__ */ import_react7.default.createElement(import_lucide_react5.TriangleAlert, { className: "w-4 h-4 flex-shrink-0" }), /* @__PURE__ */ import_react7.default.createElement("span", null, error)), /* @__PURE__ */ import_react7.default.createElement("div", { ref: bottomRef })),
745
- /* @__PURE__ */ import_react7.default.createElement("div", { className: `px-4 pb-4 pt-2 border-t border-slate-200 dark:border-white/10 ${isGlass ? "bg-transparent" : "bg-white dark:bg-[#0f0f1a]"}` }, /* @__PURE__ */ import_react7.default.createElement("div", { className: `flex items-end gap-2 bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 p-2 focus-within:border-slate-300 dark:focus-within:border-white/20 transition-all ${ui.borderRadius === "full" ? "rounded-3xl" : "rounded-2xl"}` }, /* @__PURE__ */ import_react7.default.createElement(
828
+ /* @__PURE__ */ import_react7.default.createElement("div", { className: `px-4 pb-4 pt-2 border-t border-slate-200 dark:border-white/10 ${isGlass ? "bg-transparent" : "bg-white dark:bg-[#0f0f1a]"}` }, (suggestions.length > 0 || isSuggesting) && !isLoading && /* @__PURE__ */ import_react7.default.createElement("div", { className: "mb-3 flex flex-wrap gap-2 animate-in fade-in slide-in-from-bottom-2 duration-300" }, suggestions.map((suggestion) => /* @__PURE__ */ import_react7.default.createElement(
829
+ "button",
830
+ {
831
+ key: suggestion,
832
+ onClick: () => {
833
+ var _a2;
834
+ setInput(suggestion);
835
+ setSuggestions([]);
836
+ (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
837
+ },
838
+ className: "flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-[11px] font-medium bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/60 hover:text-slate-900 dark:hover:text-white/90 hover:border-slate-300 dark:hover:border-white/20 transition-all shadow-sm cursor-pointer group"
839
+ },
840
+ /* @__PURE__ */ import_react7.default.createElement(import_lucide_react5.Sparkles, { className: "w-3 h-3 text-amber-400" }),
841
+ suggestion
842
+ )), isSuggesting && suggestions.length === 0 && /* @__PURE__ */ import_react7.default.createElement("div", { className: "flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-[11px] font-medium text-slate-400 dark:text-white/30 animate-pulse" }, /* @__PURE__ */ import_react7.default.createElement(import_lucide_react5.Sparkles, { className: "w-3 h-3" }), "Thinking...")), /* @__PURE__ */ import_react7.default.createElement("div", { className: `flex items-end gap-2 bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 p-2 focus-within:border-slate-300 dark:focus-within:border-white/20 transition-all ${ui.borderRadius === "full" ? "rounded-3xl" : "rounded-2xl"}` }, /* @__PURE__ */ import_react7.default.createElement(
746
843
  "textarea",
747
844
  {
748
845
  ref: inputRef,
749
846
  value: input,
750
- onChange: (e) => setInput(e.target.value),
847
+ onChange: (e) => {
848
+ const val = e.target.value;
849
+ setInput(val);
850
+ if (val.trim().length < 3) {
851
+ setSuggestions([]);
852
+ }
853
+ },
751
854
  onKeyDown: handleKeyDown,
752
855
  placeholder: ui.placeholder,
753
856
  rows: 1,
@@ -755,6 +858,15 @@ function ChatWindow({
755
858
  className: "flex-1 bg-transparent text-slate-900 dark:text-white/90 placeholder-slate-400 dark:placeholder-white/30 text-sm resize-none outline-none py-1.5 px-2 max-h-32 leading-relaxed disabled:opacity-50",
756
859
  style: { scrollbarWidth: "none" }
757
860
  }
861
+ ), ui.enableVoiceInput !== false && /* @__PURE__ */ import_react7.default.createElement(
862
+ "button",
863
+ {
864
+ type: "button",
865
+ onClick: toggleListening,
866
+ className: `flex-shrink-0 w-9 h-9 rounded-xl flex items-center justify-center transition-all hover:scale-105 active:scale-95 ${isListening ? "bg-rose-500 text-white animate-pulse shadow-md shadow-rose-500/20" : "text-slate-400 dark:text-white/40 hover:bg-slate-200 dark:hover:bg-white/10"}`,
867
+ title: isListening ? "Stop listening" : "Start voice input"
868
+ },
869
+ isListening ? /* @__PURE__ */ import_react7.default.createElement(import_lucide_react5.MicOff, { className: "w-4 h-4" }) : /* @__PURE__ */ import_react7.default.createElement(import_lucide_react5.Mic, { className: "w-4 h-4" })
758
870
  ), /* @__PURE__ */ import_react7.default.createElement(
759
871
  "button",
760
872
  {
package/dist/index.mjs CHANGED
@@ -21,7 +21,9 @@ import {
21
21
  TriangleAlert,
22
22
  RotateCcw,
23
23
  Maximize2,
24
- Minimize2
24
+ Minimize2,
25
+ Mic,
26
+ MicOff
25
27
  } from "lucide-react";
26
28
 
27
29
  // src/components/MessageBubble.tsx
@@ -281,7 +283,9 @@ function MessageBubble({
281
283
 
282
284
  // src/components/ConfigProvider.tsx
283
285
  import React5, { createContext, useContext } from "react";
284
- var defaultConfig = {
286
+
287
+ // src/config/uiConstants.ts
288
+ var DEFAULT_CONFIG = {
285
289
  projectId: "default",
286
290
  ui: {
287
291
  title: "AI Assistant",
@@ -297,17 +301,33 @@ var defaultConfig = {
297
301
  visualStyle: "glass",
298
302
  borderRadius: "xl",
299
303
  allowUpload: true,
300
- allowResize: true
304
+ allowResize: true,
305
+ enableVoiceInput: true
301
306
  }
302
307
  };
303
- var ConfigContext = createContext(defaultConfig);
308
+ var BORDER_RADIUS_MAP = {
309
+ none: "rounded-none",
310
+ sm: "rounded-sm",
311
+ md: "rounded-md",
312
+ lg: "rounded-lg",
313
+ xl: "rounded-xl",
314
+ full: "rounded-3xl"
315
+ };
316
+ var CHAT_SUGGESTIONS = [
317
+ "What can you help me with?",
318
+ "Summarise the key topics",
319
+ "Show me an example"
320
+ ];
321
+
322
+ // src/components/ConfigProvider.tsx
323
+ var ConfigContext = createContext(DEFAULT_CONFIG);
304
324
  function ConfigProvider({
305
325
  config,
306
326
  children
307
327
  }) {
308
328
  const merged = {
309
- projectId: (config == null ? void 0 : config.projectId) || defaultConfig.projectId,
310
- ui: mergeDefined(defaultConfig.ui, config == null ? void 0 : config.ui)
329
+ projectId: (config == null ? void 0 : config.projectId) || DEFAULT_CONFIG.projectId,
330
+ ui: mergeDefined(DEFAULT_CONFIG.ui, config == null ? void 0 : config.ui)
311
331
  };
312
332
  return /* @__PURE__ */ React5.createElement(ConfigContext.Provider, { value: merged }, children);
313
333
  }
@@ -505,21 +525,6 @@ function useRagChat(projectId, options = {}) {
505
525
  };
506
526
  }
507
527
 
508
- // src/config/uiConstants.ts
509
- var BORDER_RADIUS_MAP = {
510
- none: "rounded-none",
511
- sm: "rounded-sm",
512
- md: "rounded-md",
513
- lg: "rounded-lg",
514
- xl: "rounded-xl",
515
- full: "rounded-3xl"
516
- };
517
- var CHAT_SUGGESTIONS = [
518
- "What can you help me with?",
519
- "Summarise the key topics",
520
- "Show me an example"
521
- ];
522
-
523
528
  // src/components/ChatWindow.tsx
524
529
  function ChatWindow({
525
530
  className = "",
@@ -542,6 +547,56 @@ function ChatWindow({
542
547
  const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
543
548
  namespace: projectId
544
549
  });
550
+ const [suggestions, setSuggestions] = useState4([]);
551
+ const [isSuggesting, setIsSuggesting] = useState4(false);
552
+ const [isListening, setIsListening] = useState4(false);
553
+ const recognitionRef = useRef3(null);
554
+ useEffect4(() => {
555
+ if (typeof window !== "undefined") {
556
+ const win = window;
557
+ const SpeechRecognition = win.SpeechRecognition || win.webkitSpeechRecognition;
558
+ if (SpeechRecognition) {
559
+ const recognition = new SpeechRecognition();
560
+ recognition.continuous = true;
561
+ recognition.interimResults = true;
562
+ recognition.onresult = (event) => {
563
+ let finalTranscript = "";
564
+ for (let i = event.resultIndex; i < event.results.length; ++i) {
565
+ if (event.results[i].isFinal) {
566
+ finalTranscript += event.results[i][0].transcript;
567
+ }
568
+ }
569
+ if (finalTranscript) {
570
+ setInput((prev) => (prev ? prev + " " : "") + finalTranscript.trim());
571
+ }
572
+ };
573
+ recognition.onerror = (event) => {
574
+ console.error("Speech recognition error:", event.error);
575
+ setIsListening(false);
576
+ };
577
+ recognition.onend = () => {
578
+ setIsListening(false);
579
+ };
580
+ recognitionRef.current = recognition;
581
+ }
582
+ }
583
+ }, []);
584
+ const toggleListening = () => {
585
+ if (!recognitionRef.current) {
586
+ alert("Speech recognition is not supported in your browser.");
587
+ return;
588
+ }
589
+ if (isListening) {
590
+ recognitionRef.current.stop();
591
+ } else {
592
+ try {
593
+ recognitionRef.current.start();
594
+ setIsListening(true);
595
+ } catch (err) {
596
+ console.error("Failed to start recognition:", err);
597
+ }
598
+ }
599
+ };
545
600
  useEffect4(() => {
546
601
  setMounted(true);
547
602
  }, []);
@@ -552,15 +607,21 @@ function ChatWindow({
552
607
  }
553
608
  }, [messages, isLoading]);
554
609
  const sendMessage = useCallback2(async () => {
610
+ var _a2;
555
611
  const text = input.trim();
556
612
  if (!text || isLoading) return;
613
+ if (isListening) {
614
+ (_a2 = recognitionRef.current) == null ? void 0 : _a2.stop();
615
+ }
557
616
  setInput("");
617
+ setSuggestions([]);
618
+ setIsSuggesting(false);
558
619
  await send(text);
559
620
  window.setTimeout(() => {
560
- var _a2;
561
- return (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
621
+ var _a3;
622
+ return (_a3 = inputRef.current) == null ? void 0 : _a3.focus();
562
623
  }, 50);
563
- }, [input, isLoading, send]);
624
+ }, [input, isLoading, send, isListening]);
564
625
  const handleKeyDown = (e) => {
565
626
  if (e.key === "Enter" && !e.shiftKey) {
566
627
  e.preventDefault();
@@ -569,10 +630,36 @@ function ChatWindow({
569
630
  };
570
631
  const clearHistory = () => {
571
632
  clear();
633
+ setSuggestions([]);
634
+ setIsSuggesting(false);
572
635
  };
573
636
  const isEmpty = messages.length === 0;
574
637
  const currentRadius = BORDER_RADIUS_MAP[ui.borderRadius || "xl"];
575
638
  const isGlass = ui.visualStyle !== "solid";
639
+ useEffect4(() => {
640
+ if (input.trim().length < 3) {
641
+ return;
642
+ }
643
+ const timer = setTimeout(async () => {
644
+ setIsSuggesting(true);
645
+ try {
646
+ const response = await fetch("/api/suggestions", {
647
+ method: "POST",
648
+ headers: { "Content-Type": "application/json" },
649
+ body: JSON.stringify({ query: input, namespace: projectId })
650
+ });
651
+ const data = await response.json();
652
+ if (data.suggestions) {
653
+ setSuggestions(data.suggestions);
654
+ }
655
+ } catch (err) {
656
+ console.error("Failed to fetch suggestions:", err);
657
+ } finally {
658
+ setIsSuggesting(false);
659
+ }
660
+ }, 800);
661
+ return () => clearTimeout(timer);
662
+ }, [input, projectId]);
576
663
  return /* @__PURE__ */ React7.createElement(
577
664
  "div",
578
665
  {
@@ -677,19 +764,39 @@ function ChatWindow({
677
764
  )), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */ React7.createElement(
678
765
  MessageBubble,
679
766
  {
680
- message: { role: "assistant", content: "" },
767
+ message: { id: "loading", role: "assistant", content: "", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
681
768
  isStreaming: true,
682
769
  primaryColor: ui.primaryColor,
683
770
  accentColor: ui.accentColor,
684
771
  onAddToCart
685
772
  }
686
773
  ), error && /* @__PURE__ */ React7.createElement("div", { className: "flex items-center gap-2 text-rose-500 dark:text-rose-400 text-sm bg-rose-50 dark:bg-rose-400/10 border border-rose-200 dark:border-rose-400/20 rounded-xl px-4 py-3" }, /* @__PURE__ */ React7.createElement(TriangleAlert, { className: "w-4 h-4 flex-shrink-0" }), /* @__PURE__ */ React7.createElement("span", null, error)), /* @__PURE__ */ React7.createElement("div", { ref: bottomRef })),
687
- /* @__PURE__ */ React7.createElement("div", { className: `px-4 pb-4 pt-2 border-t border-slate-200 dark:border-white/10 ${isGlass ? "bg-transparent" : "bg-white dark:bg-[#0f0f1a]"}` }, /* @__PURE__ */ React7.createElement("div", { className: `flex items-end gap-2 bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 p-2 focus-within:border-slate-300 dark:focus-within:border-white/20 transition-all ${ui.borderRadius === "full" ? "rounded-3xl" : "rounded-2xl"}` }, /* @__PURE__ */ React7.createElement(
774
+ /* @__PURE__ */ React7.createElement("div", { className: `px-4 pb-4 pt-2 border-t border-slate-200 dark:border-white/10 ${isGlass ? "bg-transparent" : "bg-white dark:bg-[#0f0f1a]"}` }, (suggestions.length > 0 || isSuggesting) && !isLoading && /* @__PURE__ */ React7.createElement("div", { className: "mb-3 flex flex-wrap gap-2 animate-in fade-in slide-in-from-bottom-2 duration-300" }, suggestions.map((suggestion) => /* @__PURE__ */ React7.createElement(
775
+ "button",
776
+ {
777
+ key: suggestion,
778
+ onClick: () => {
779
+ var _a2;
780
+ setInput(suggestion);
781
+ setSuggestions([]);
782
+ (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
783
+ },
784
+ className: "flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-[11px] font-medium bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/60 hover:text-slate-900 dark:hover:text-white/90 hover:border-slate-300 dark:hover:border-white/20 transition-all shadow-sm cursor-pointer group"
785
+ },
786
+ /* @__PURE__ */ React7.createElement(Sparkles, { className: "w-3 h-3 text-amber-400" }),
787
+ suggestion
788
+ )), isSuggesting && suggestions.length === 0 && /* @__PURE__ */ React7.createElement("div", { className: "flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-[11px] font-medium text-slate-400 dark:text-white/30 animate-pulse" }, /* @__PURE__ */ React7.createElement(Sparkles, { className: "w-3 h-3" }), "Thinking...")), /* @__PURE__ */ React7.createElement("div", { className: `flex items-end gap-2 bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 p-2 focus-within:border-slate-300 dark:focus-within:border-white/20 transition-all ${ui.borderRadius === "full" ? "rounded-3xl" : "rounded-2xl"}` }, /* @__PURE__ */ React7.createElement(
688
789
  "textarea",
689
790
  {
690
791
  ref: inputRef,
691
792
  value: input,
692
- onChange: (e) => setInput(e.target.value),
793
+ onChange: (e) => {
794
+ const val = e.target.value;
795
+ setInput(val);
796
+ if (val.trim().length < 3) {
797
+ setSuggestions([]);
798
+ }
799
+ },
693
800
  onKeyDown: handleKeyDown,
694
801
  placeholder: ui.placeholder,
695
802
  rows: 1,
@@ -697,6 +804,15 @@ function ChatWindow({
697
804
  className: "flex-1 bg-transparent text-slate-900 dark:text-white/90 placeholder-slate-400 dark:placeholder-white/30 text-sm resize-none outline-none py-1.5 px-2 max-h-32 leading-relaxed disabled:opacity-50",
698
805
  style: { scrollbarWidth: "none" }
699
806
  }
807
+ ), ui.enableVoiceInput !== false && /* @__PURE__ */ React7.createElement(
808
+ "button",
809
+ {
810
+ type: "button",
811
+ onClick: toggleListening,
812
+ className: `flex-shrink-0 w-9 h-9 rounded-xl flex items-center justify-center transition-all hover:scale-105 active:scale-95 ${isListening ? "bg-rose-500 text-white animate-pulse shadow-md shadow-rose-500/20" : "text-slate-400 dark:text-white/40 hover:bg-slate-200 dark:hover:bg-white/10"}`,
813
+ title: isListening ? "Stop listening" : "Start voice input"
814
+ },
815
+ isListening ? /* @__PURE__ */ React7.createElement(MicOff, { className: "w-4 h-4" }) : /* @__PURE__ */ React7.createElement(Mic, { className: "w-4 h-4" })
700
816
  ), /* @__PURE__ */ React7.createElement(
701
817
  "button",
702
818
  {
package/dist/server.d.mts CHANGED
@@ -1,7 +1,8 @@
1
- import { i as VectorDBConfig, L as LLMConfig, c as EmbeddingConfig, g as RagConfig, e as IngestDocument, C as ChatMessage, b as ChatResponse, k as RetrievalResult, h as UpsertDocument, V as VectorMatch, G as GraphDBConfig, l as GraphNode, m as Edge, n as GraphSearchResult, I as ILLMProvider, j as VectorDBProvider, f as LLMProvider, d as EmbeddingProvider, R as RAGConfig, U as UIConfig, a as ChatOptions, E as EmbedOptions } from './RagConfig-FyMB_UG6.mjs';
2
- export { C as Chunk, a as ChunkOptions, D as DocumentChunker } from './DocumentChunker-Dh9TvmGG.mjs';
3
- import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-D-lfcqlL.mjs';
4
- export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from './index-D-lfcqlL.mjs';
1
+ import { j as VectorDBConfig, L as LLMConfig, E as EmbeddingConfig, h as RagConfig, I as IngestDocument, C as ChatMessage, d as ChatResponse, l as RetrievalResult, i as UpsertDocument, V as VectorMatch, G as GraphDBConfig, m as GraphNode, n as Edge, o as GraphSearchResult, k as VectorDBProvider, f as LLMProvider, e as EmbeddingProvider, g as RAGConfig, U as UIConfig, c as ChatOptions } from './index-Cti1u0y1.mjs';
2
+ import { I as ILLMProvider, E as EmbedOptions } from './DocumentChunker-D1dg5iCi.mjs';
3
+ export { C as Chunk, a as ChunkOptions, D as DocumentChunker } from './DocumentChunker-D1dg5iCi.mjs';
4
+ import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-kUXnRvuI.mjs';
5
+ export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from './index-kUXnRvuI.mjs';
5
6
  import 'next/server';
6
7
 
7
8
  /**
@@ -166,6 +167,10 @@ declare class Pipeline {
166
167
  * Rewrite the user query for better retrieval performance.
167
168
  */
168
169
  private rewriteQuery;
170
+ /**
171
+ * Generate 3-5 short, relevant questions based on the vector database content.
172
+ */
173
+ getSuggestions(query: string, namespace?: string): Promise<string[]>;
169
174
  }
170
175
 
171
176
  /**
package/dist/server.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { i as VectorDBConfig, L as LLMConfig, c as EmbeddingConfig, g as RagConfig, e as IngestDocument, C as ChatMessage, b as ChatResponse, k as RetrievalResult, h as UpsertDocument, V as VectorMatch, G as GraphDBConfig, l as GraphNode, m as Edge, n as GraphSearchResult, I as ILLMProvider, j as VectorDBProvider, f as LLMProvider, d as EmbeddingProvider, R as RAGConfig, U as UIConfig, a as ChatOptions, E as EmbedOptions } from './RagConfig-FyMB_UG6.js';
2
- export { C as Chunk, a as ChunkOptions, D as DocumentChunker } from './DocumentChunker-Dh9TvmGG.js';
3
- import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-CYgr00ot.js';
4
- export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from './index-CYgr00ot.js';
1
+ import { j as VectorDBConfig, L as LLMConfig, E as EmbeddingConfig, h as RagConfig, I as IngestDocument, C as ChatMessage, d as ChatResponse, l as RetrievalResult, i as UpsertDocument, V as VectorMatch, G as GraphDBConfig, m as GraphNode, n as Edge, o as GraphSearchResult, k as VectorDBProvider, f as LLMProvider, e as EmbeddingProvider, g as RAGConfig, U as UIConfig, c as ChatOptions } from './index-Cti1u0y1.js';
2
+ import { I as ILLMProvider, E as EmbedOptions } from './DocumentChunker-BXOUMKoP.js';
3
+ export { C as Chunk, a as ChunkOptions, D as DocumentChunker } from './DocumentChunker-BXOUMKoP.js';
4
+ import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-B67KQ9NN.js';
5
+ export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from './index-B67KQ9NN.js';
5
6
  import 'next/server';
6
7
 
7
8
  /**
@@ -166,6 +167,10 @@ declare class Pipeline {
166
167
  * Rewrite the user query for better retrieval performance.
167
168
  */
168
169
  private rewriteQuery;
170
+ /**
171
+ * Generate 3-5 short, relevant questions based on the vector database content.
172
+ */
173
+ getSuggestions(query: string, namespace?: string): Promise<string[]>;
169
174
  }
170
175
 
171
176
  /**
package/dist/server.js CHANGED
@@ -4177,6 +4177,50 @@ Optimized Search Query:`;
4177
4177
  );
4178
4178
  return rewrite.trim() || question;
4179
4179
  }
4180
+ /**
4181
+ * Generate 3-5 short, relevant questions based on the vector database content.
4182
+ */
4183
+ async getSuggestions(query, namespace) {
4184
+ if (!query || query.trim().length < 3) {
4185
+ return [];
4186
+ }
4187
+ await this.initialize();
4188
+ const ns = namespace != null ? namespace : this.config.projectId;
4189
+ try {
4190
+ const { sources } = await this.retrieve(query, { namespace: ns, topK: 3 });
4191
+ if (sources.length === 0) {
4192
+ return [];
4193
+ }
4194
+ const context = sources.map((s) => s.content).join("\n\n---\n\n");
4195
+ const prompt = `
4196
+ Based on the following snippets from a document, what are 3 short, relevant questions a user might ask?
4197
+ Focus on questions that can be answered by the context.
4198
+ Keep each question under 10 words and make them very specific to the content.
4199
+ Return ONLY a JSON array of strings like ["Question 1", "Question 2", "Question 3"].
4200
+
4201
+ Context:
4202
+ ${context}
4203
+
4204
+ Suggestions:`;
4205
+ const response = await this.llmProvider.chat(
4206
+ [
4207
+ { role: "system", content: "You are a helpful assistant that generates search suggestions." },
4208
+ { role: "user", content: prompt }
4209
+ ],
4210
+ ""
4211
+ );
4212
+ const match = response.match(/\[[\s\S]*\]/);
4213
+ if (match) {
4214
+ const suggestions = JSON.parse(match[0]);
4215
+ if (Array.isArray(suggestions)) {
4216
+ return suggestions.map((s) => String(s)).slice(0, 3);
4217
+ }
4218
+ }
4219
+ } catch (error) {
4220
+ console.error("[Pipeline] Failed to generate suggestions:", error);
4221
+ }
4222
+ return [];
4223
+ }
4180
4224
  };
4181
4225
 
4182
4226
  // src/core/ProviderHealthCheck.ts
@@ -4331,6 +4375,13 @@ var VectorPlugin = class {
4331
4375
  await this.validationPromise;
4332
4376
  return this.pipeline.ingest(documents, namespace);
4333
4377
  }
4378
+ /**
4379
+ * Get auto-suggestions based on a query prefix.
4380
+ */
4381
+ async getSuggestions(query, namespace) {
4382
+ await this.validationPromise;
4383
+ return this.pipeline.getSuggestions(query, namespace);
4384
+ }
4334
4385
  };
4335
4386
 
4336
4387
  // src/config/ConfigBuilder.ts
package/dist/server.mjs CHANGED
@@ -39,7 +39,7 @@ import {
39
39
  sseFrame,
40
40
  sseMetaFrame,
41
41
  sseTextFrame
42
- } from "./chunk-OCDCJUNE.mjs";
42
+ } from "./chunk-GCPPRD2G.mjs";
43
43
  import "./chunk-YLTMFW4M.mjs";
44
44
  import {
45
45
  PineconeProvider
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retrivora-ai/rag-engine",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
5
5
  "author": "Abhinav Alkuchi",
6
6
  "license": "MIT",
@@ -0,0 +1,4 @@
1
+ import { createSuggestionsHandler } from '@/handlers';
2
+ import { getRagConfig } from '@/config/serverConfig';
3
+
4
+ export const POST = createSuggestionsHandler(getRagConfig());
@@ -4,13 +4,7 @@ import React, { useState } from 'react';
4
4
  import { MessageSquare, X } from 'lucide-react';
5
5
  import { ChatWindow } from './ChatWindow';
6
6
  import { useConfig } from './ConfigProvider';
7
-
8
- interface ChatWidgetProps {
9
- /** Position of the floating button. Defaults to bottom-right. */
10
- position?: 'bottom-right' | 'bottom-left';
11
- /** Called when the user clicks 'Add to Cart' on a product */
12
- onAddToCart?: (product: any) => void;
13
- }
7
+ import { ChatWidgetProps } from '../types';
14
8
 
15
9
  const DEFAULT_DIMENSIONS = { width: 380, height: 600 };
16
10