sagedesk 2.1.1 → 2.3.0
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/README.md +17 -38
- package/dist/next/{SageDeskWidget-SJVE6QK3.js → SageDeskWidget-ZJJGXTTC.js} +66 -37
- package/dist/next/SageDeskWidget-ZJJGXTTC.js.map +1 -0
- package/dist/next/index.cjs +142 -114
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.js +1 -1
- package/dist/react/index.cjs +63 -35
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +1 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +65 -36
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs +21 -120
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +13 -7
- package/dist/server/index.d.ts +13 -7
- package/dist/server/index.js +21 -110
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/next/SageDeskWidget-SJVE6QK3.js.map +0 -1
package/dist/react/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// src/react/SageDeskWidget.tsx
|
|
2
|
-
import {
|
|
2
|
+
import React, {
|
|
3
3
|
useState as useState2,
|
|
4
4
|
useRef as useRef2,
|
|
5
5
|
useEffect as useEffect2,
|
|
6
|
-
useCallback as useCallback2
|
|
6
|
+
useCallback as useCallback2,
|
|
7
|
+
useMemo as useMemo2
|
|
7
8
|
} from "react";
|
|
8
9
|
import { createPortal } from "react-dom";
|
|
9
10
|
|
|
@@ -263,6 +264,7 @@ function logFallbackWarning(reason) {
|
|
|
263
264
|
}
|
|
264
265
|
var initialState = {
|
|
265
266
|
messages: [],
|
|
267
|
+
userMessages: [],
|
|
266
268
|
isOpen: false,
|
|
267
269
|
isTyping: false,
|
|
268
270
|
engineStatus: "idle",
|
|
@@ -275,8 +277,11 @@ function reducer(state, action) {
|
|
|
275
277
|
return { ...state, isOpen: true };
|
|
276
278
|
case "CLOSE":
|
|
277
279
|
return { ...state, isOpen: false };
|
|
278
|
-
case "ADD_MESSAGE":
|
|
279
|
-
|
|
280
|
+
case "ADD_MESSAGE": {
|
|
281
|
+
const newMessages = [...state.messages, action.payload];
|
|
282
|
+
const newUserMessages = action.payload.role === "user" ? [...state.userMessages, action.payload] : state.userMessages;
|
|
283
|
+
return { ...state, messages: newMessages, userMessages: newUserMessages };
|
|
284
|
+
}
|
|
280
285
|
case "SET_TYPING":
|
|
281
286
|
return { ...state, isTyping: action.payload };
|
|
282
287
|
case "SET_ENGINE_STATUS":
|
|
@@ -299,6 +304,7 @@ function useSageDesk(config) {
|
|
|
299
304
|
const embedderRef = useRef(null);
|
|
300
305
|
const engineStartedRef = useRef(false);
|
|
301
306
|
const msgCounterRef = useRef(0);
|
|
307
|
+
const engineReadyCallbacksRef = useRef([]);
|
|
302
308
|
const [chips, setChips] = useState([]);
|
|
303
309
|
const makeId = () => `msg-${++msgCounterRef.current}`;
|
|
304
310
|
const addMessage = useCallback(
|
|
@@ -310,12 +316,27 @@ function useSageDesk(config) {
|
|
|
310
316
|
},
|
|
311
317
|
[]
|
|
312
318
|
);
|
|
319
|
+
const notifyEngineReady = useCallback(() => {
|
|
320
|
+
const callbacks = engineReadyCallbacksRef.current;
|
|
321
|
+
engineReadyCallbacksRef.current = [];
|
|
322
|
+
callbacks.forEach((cb) => cb());
|
|
323
|
+
}, []);
|
|
313
324
|
const startEngine = useCallback(async () => {
|
|
314
325
|
if (engineStartedRef.current) return;
|
|
315
326
|
engineStartedRef.current = true;
|
|
316
327
|
if (config.mode === "llm") {
|
|
317
328
|
setChips(config.agent.suggestedChips ?? []);
|
|
318
|
-
dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "
|
|
329
|
+
dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "loading-model" } });
|
|
330
|
+
try {
|
|
331
|
+
embedderRef.current = new EmbedderRuntime();
|
|
332
|
+
await embedderRef.current.load(config.agent.model);
|
|
333
|
+
dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "ready" } });
|
|
334
|
+
notifyEngineReady();
|
|
335
|
+
} catch (err) {
|
|
336
|
+
console.warn("[sagedesk] WASM embedder failed to load - LLM mode will use fallback messages.", err);
|
|
337
|
+
dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "error-model", error: String(err) } });
|
|
338
|
+
notifyEngineReady();
|
|
339
|
+
}
|
|
319
340
|
return;
|
|
320
341
|
}
|
|
321
342
|
dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "loading-index" } });
|
|
@@ -327,6 +348,7 @@ function useSageDesk(config) {
|
|
|
327
348
|
type: "SET_ENGINE_STATUS",
|
|
328
349
|
payload: { status: "error-index", error: String(err) }
|
|
329
350
|
});
|
|
351
|
+
notifyEngineReady();
|
|
330
352
|
addMessage({
|
|
331
353
|
role: "bot",
|
|
332
354
|
text: "I'm having trouble loading right now. Please try again in a moment."
|
|
@@ -339,12 +361,14 @@ function useSageDesk(config) {
|
|
|
339
361
|
embedderRef.current = new EmbedderRuntime();
|
|
340
362
|
await embedderRef.current.load(config.agent.model);
|
|
341
363
|
dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "ready" } });
|
|
364
|
+
notifyEngineReady();
|
|
342
365
|
} catch (err) {
|
|
343
366
|
console.warn("[sagedesk] WASM model failed to load, falling back to keyword search -", err);
|
|
344
367
|
embedderRef.current = new EmbedderRuntime();
|
|
345
368
|
dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "degraded" } });
|
|
369
|
+
notifyEngineReady();
|
|
346
370
|
}
|
|
347
|
-
}, [config.mode, config.indexUrl, config.agent.suggestedChips, addMessage]);
|
|
371
|
+
}, [config.mode, config.indexUrl, config.agent.model, config.agent.suggestedChips, addMessage, notifyEngineReady]);
|
|
348
372
|
const greetingShownRef = useRef(false);
|
|
349
373
|
const open = useCallback(() => {
|
|
350
374
|
dispatch({ type: "OPEN" });
|
|
@@ -361,16 +385,12 @@ function useSageDesk(config) {
|
|
|
361
385
|
dispatch({ type: "CLOSE" });
|
|
362
386
|
}, []);
|
|
363
387
|
const waitForEngine = useCallback(() => {
|
|
388
|
+
const s = engineStatusRef.current;
|
|
389
|
+
if (s === "ready" || s === "degraded" || s === "error-index" || s === "error-model") {
|
|
390
|
+
return Promise.resolve();
|
|
391
|
+
}
|
|
364
392
|
return new Promise((resolve) => {
|
|
365
|
-
|
|
366
|
-
const s = engineStatusRef.current;
|
|
367
|
-
if (s === "ready" || s === "degraded" || s === "error-index" || s === "error-model") {
|
|
368
|
-
resolve();
|
|
369
|
-
} else {
|
|
370
|
-
setTimeout(check, 100);
|
|
371
|
-
}
|
|
372
|
-
};
|
|
373
|
-
check();
|
|
393
|
+
engineReadyCallbacksRef.current.push(resolve);
|
|
374
394
|
});
|
|
375
395
|
}, []);
|
|
376
396
|
const submit = useCallback(
|
|
@@ -394,12 +414,20 @@ function useSageDesk(config) {
|
|
|
394
414
|
console.warn('[sagedesk] LLM mode requires an "endpoint" prop.');
|
|
395
415
|
botText = getFallback(config.agent);
|
|
396
416
|
isFallback = true;
|
|
417
|
+
} else if (!embedderRef.current?.isReady) {
|
|
418
|
+
console.warn("[sagedesk] Embedder not ready - showing fallback.");
|
|
419
|
+
botText = getFallback(config.agent);
|
|
420
|
+
isFallback = true;
|
|
397
421
|
} else {
|
|
398
422
|
try {
|
|
423
|
+
const vector = await embedderRef.current.embed(trimmed);
|
|
399
424
|
const res = await fetch(config.endpoint, {
|
|
400
425
|
method: "POST",
|
|
401
426
|
headers: { "Content-Type": "application/json" },
|
|
402
|
-
body: JSON.stringify({
|
|
427
|
+
body: JSON.stringify({
|
|
428
|
+
query: trimmed,
|
|
429
|
+
queryVector: Array.from(vector)
|
|
430
|
+
})
|
|
403
431
|
});
|
|
404
432
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
405
433
|
const data = await res.json();
|
|
@@ -443,8 +471,8 @@ function useSageDesk(config) {
|
|
|
443
471
|
}
|
|
444
472
|
if (config.mode !== "llm") {
|
|
445
473
|
const elapsed = Date.now() - typingStart;
|
|
446
|
-
const delayBase = retrievalMode === "keyword" || isFallback ?
|
|
447
|
-
const minTypingMs = delayBase + Math.random() *
|
|
474
|
+
const delayBase = retrievalMode === "keyword" || isFallback ? 400 : 800;
|
|
475
|
+
const minTypingMs = delayBase + Math.random() * 400;
|
|
448
476
|
const remaining = minTypingMs - elapsed;
|
|
449
477
|
if (remaining > 0) await new Promise((r) => setTimeout(r, remaining));
|
|
450
478
|
}
|
|
@@ -462,11 +490,9 @@ function useSageDesk(config) {
|
|
|
462
490
|
return () => document.removeEventListener("keydown", handler);
|
|
463
491
|
}, [state.isOpen, close]);
|
|
464
492
|
const activeChips = useMemo(() => {
|
|
465
|
-
const askedTexts = new Set(
|
|
466
|
-
state.messages.filter((m) => m.role === "user").map((m) => m.text.toLowerCase().trim())
|
|
467
|
-
);
|
|
493
|
+
const askedTexts = new Set(state.userMessages.map((m) => m.text.toLowerCase().trim()));
|
|
468
494
|
return chips.filter((chip) => !askedTexts.has(chip.toLowerCase().trim()));
|
|
469
|
-
}, [chips, state.
|
|
495
|
+
}, [chips, state.userMessages]);
|
|
470
496
|
return { state, chips: activeChips, open, close, submit };
|
|
471
497
|
}
|
|
472
498
|
|
|
@@ -662,9 +688,9 @@ var PoweredBy = ({ dark = false }) => /* @__PURE__ */ jsxs("div", { style: {
|
|
|
662
688
|
}
|
|
663
689
|
)
|
|
664
690
|
] });
|
|
665
|
-
|
|
691
|
+
var ClassicMessageBubble = React.memo(function ClassicMessageBubble2({ msg, accent }) {
|
|
666
692
|
const isBot = msg.role === "bot";
|
|
667
|
-
const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
|
|
693
|
+
const renderedHtml = useMemo2(() => isBot ? parseMarkdown(msg.text) : msg.text, [isBot, msg.text]);
|
|
668
694
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: isBot ? "flex-start" : "flex-end", gap: "4px" }, children: [
|
|
669
695
|
msg.isFallback && /* @__PURE__ */ jsx("p", { style: { fontSize: "11px", fontWeight: 500, color: "#9b9aa3", margin: 0, padding: "0 4px", fontFamily: "inherit" }, children: "Not sure about that one" }),
|
|
670
696
|
/* @__PURE__ */ jsx("div", { style: {
|
|
@@ -689,7 +715,7 @@ function ClassicMessageBubble({ msg, accent }) {
|
|
|
689
715
|
fontFamily: "inherit"
|
|
690
716
|
}, children: "just now" })
|
|
691
717
|
] });
|
|
692
|
-
}
|
|
718
|
+
});
|
|
693
719
|
function ClassicTypingIndicator() {
|
|
694
720
|
const dot = { width: 6, height: 6, borderRadius: "50%", background: "#c8c8ce", display: "inline-block" };
|
|
695
721
|
return /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-start", gap: "4px" }, children: /* @__PURE__ */ jsxs("div", { style: {
|
|
@@ -707,9 +733,9 @@ function ClassicTypingIndicator() {
|
|
|
707
733
|
/* @__PURE__ */ jsx("span", { style: dot, className: "sd-r-dot-3" })
|
|
708
734
|
] }) });
|
|
709
735
|
}
|
|
710
|
-
|
|
736
|
+
var LightMessageBubble = React.memo(function LightMessageBubble2({ msg, accent, agentName }) {
|
|
711
737
|
const isBot = msg.role === "bot";
|
|
712
|
-
const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
|
|
738
|
+
const renderedHtml = useMemo2(() => isBot ? parseMarkdown(msg.text) : msg.text, [isBot, msg.text]);
|
|
713
739
|
if (isBot) {
|
|
714
740
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "10px" }, children: [
|
|
715
741
|
/* @__PURE__ */ jsx("div", { style: {
|
|
@@ -744,7 +770,7 @@ function LightMessageBubble({ msg, accent, agentName }) {
|
|
|
744
770
|
wordBreak: "break-word",
|
|
745
771
|
fontFamily: "inherit"
|
|
746
772
|
}, children: msg.text }) });
|
|
747
|
-
}
|
|
773
|
+
});
|
|
748
774
|
function LightTypingIndicator({ accent }) {
|
|
749
775
|
const dot = { width: 6, height: 6, borderRadius: "50%", background: "#c4c4be", display: "inline-block" };
|
|
750
776
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "10px" }, children: [
|
|
@@ -772,9 +798,9 @@ function LightTypingIndicator({ accent }) {
|
|
|
772
798
|
] })
|
|
773
799
|
] });
|
|
774
800
|
}
|
|
775
|
-
|
|
801
|
+
var DarkMessageBubble = React.memo(function DarkMessageBubble2({ msg, accent }) {
|
|
776
802
|
const isBot = msg.role === "bot";
|
|
777
|
-
const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
|
|
803
|
+
const renderedHtml = useMemo2(() => isBot ? parseMarkdown(msg.text) : msg.text, [isBot, msg.text]);
|
|
778
804
|
return /* @__PURE__ */ jsxs("div", { style: {
|
|
779
805
|
maxWidth: "85%",
|
|
780
806
|
padding: "12px 14px",
|
|
@@ -792,7 +818,7 @@ function DarkMessageBubble({ msg, accent }) {
|
|
|
792
818
|
msg.isFallback && /* @__PURE__ */ jsx("span", { style: { fontSize: "11px", color: "rgba(255,255,255,0.5)", display: "block", marginBottom: "4px" }, children: "Not sure about that one" }),
|
|
793
819
|
isBot ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: renderedHtml } }) : /* @__PURE__ */ jsx("div", { style: { whiteSpace: "pre-wrap" }, children: msg.text })
|
|
794
820
|
] });
|
|
795
|
-
}
|
|
821
|
+
});
|
|
796
822
|
function DarkTypingIndicator() {
|
|
797
823
|
const dot = { width: 6, height: 6, borderRadius: "50%", background: "rgba(255,255,255,0.4)", display: "inline-block" };
|
|
798
824
|
return /* @__PURE__ */ jsxs("div", { style: {
|
|
@@ -811,7 +837,7 @@ function DarkTypingIndicator() {
|
|
|
811
837
|
/* @__PURE__ */ jsx("span", { style: dot, className: "sd-r-dot-3" })
|
|
812
838
|
] });
|
|
813
839
|
}
|
|
814
|
-
function
|
|
840
|
+
function ClassicTheme(p) {
|
|
815
841
|
const {
|
|
816
842
|
agent,
|
|
817
843
|
state,
|
|
@@ -1000,7 +1026,7 @@ function renderClassic(p) {
|
|
|
1000
1026
|
] })
|
|
1001
1027
|
] });
|
|
1002
1028
|
}
|
|
1003
|
-
function
|
|
1029
|
+
function LightTheme(p) {
|
|
1004
1030
|
const {
|
|
1005
1031
|
agent,
|
|
1006
1032
|
state,
|
|
@@ -1188,7 +1214,7 @@ function renderLight(p) {
|
|
|
1188
1214
|
] })
|
|
1189
1215
|
] });
|
|
1190
1216
|
}
|
|
1191
|
-
function
|
|
1217
|
+
function DarkTheme(p) {
|
|
1192
1218
|
const {
|
|
1193
1219
|
agent,
|
|
1194
1220
|
state,
|
|
@@ -1425,7 +1451,10 @@ function SageDeskWidget({ mode, indexUrl, endpoint, agent, search: search2 }) {
|
|
|
1425
1451
|
'[sagedesk] Required prop "endpoint" is missing for llm mode. Provide your backend route, e.g. endpoint="/api/sagedesk".'
|
|
1426
1452
|
);
|
|
1427
1453
|
}
|
|
1428
|
-
const config =
|
|
1454
|
+
const config = useMemo2(
|
|
1455
|
+
() => ({ mode: resolvedMode, indexUrl, endpoint, agent, search: search2 }),
|
|
1456
|
+
[resolvedMode, indexUrl, endpoint, agent, search2]
|
|
1457
|
+
);
|
|
1429
1458
|
const { state, chips, open, close, submit } = useSageDesk(config);
|
|
1430
1459
|
const theme = agent.theme ?? "classic";
|
|
1431
1460
|
const accent = agent.accentColor ?? "#534AB7";
|
|
@@ -1504,7 +1533,7 @@ function SageDeskWidget({ mode, indexUrl, endpoint, agent, search: search2 }) {
|
|
|
1504
1533
|
open,
|
|
1505
1534
|
submit
|
|
1506
1535
|
};
|
|
1507
|
-
const content = theme === "dark" ?
|
|
1536
|
+
const content = theme === "dark" ? /* @__PURE__ */ jsx(DarkTheme, { ...props }) : theme === "light" ? /* @__PURE__ */ jsx(LightTheme, { ...props }) : /* @__PURE__ */ jsx(ClassicTheme, { ...props });
|
|
1508
1537
|
return createPortal(content, document.body);
|
|
1509
1538
|
}
|
|
1510
1539
|
export {
|