@iblai/web-utils 1.6.9 → 1.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5 -4
- package/dist/index.esm.js +131 -7
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +131 -7
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/web-utils/src/hooks/chat/use-advanced-chat.d.ts +2 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11040,8 +11040,16 @@ const chatSlice = createSlice({
|
|
|
11040
11040
|
state.chats = action.payload;
|
|
11041
11041
|
},
|
|
11042
11042
|
appendMessageToActiveTab: (state) => {
|
|
11043
|
-
const
|
|
11044
|
-
if
|
|
11043
|
+
const activeMessages = state.chats[state.activeTab];
|
|
11044
|
+
// Defensive: if the active tab has no messages yet (e.g. a stale
|
|
11045
|
+
// `setNewMessages([])` raced ahead of the user-message dispatch),
|
|
11046
|
+
// skip the lookup that would throw on `undefined.id` and rely on
|
|
11047
|
+
// the no-last-message branch below to append the streaming message.
|
|
11048
|
+
const lastMessageInActiveTabMessages = activeMessages && activeMessages.length > 0
|
|
11049
|
+
? activeMessages[activeMessages.length - 1]
|
|
11050
|
+
: undefined;
|
|
11051
|
+
if (!lastMessageInActiveTabMessages ||
|
|
11052
|
+
lastMessageInActiveTabMessages.id !== state.currentStreamingMessage.id) {
|
|
11045
11053
|
const temp = {
|
|
11046
11054
|
id: state.currentStreamingMessage.id,
|
|
11047
11055
|
role: "assistant",
|
|
@@ -13550,7 +13558,7 @@ function useMentorSettings({ mentorId, tenantKey, isPublicRoute = false, mainTen
|
|
|
13550
13558
|
};
|
|
13551
13559
|
}
|
|
13552
13560
|
|
|
13553
|
-
function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, token, wsUrl, stopGenerationWsUrl, redirectToAuthSpa, errorHandler, sendMessageToParentWebsite, isPreviewMode, mentorShareableToken, on402Error, cachedSessionId, onStartNewChat, onOAuthRequired, onOAuthResolved, isOffline = false, onOfflineWithoutLocalLLM, isPublicRoute, }) {
|
|
13561
|
+
function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, token, wsUrl, stopGenerationWsUrl, redirectToAuthSpa, errorHandler, sendMessageToParentWebsite, isPreviewMode, mentorShareableToken, on402Error, cachedSessionId, onStartNewChat, onOAuthRequired, onOAuthResolved, isOffline = false, onOfflineWithoutLocalLLM, isPublicRoute, initialPrompt, }) {
|
|
13554
13562
|
var _a, _b, _c, _d;
|
|
13555
13563
|
const dispatch = useDispatch();
|
|
13556
13564
|
const [createSessionId, { isLoading: isLoadingSessionIds }] = dataLayer.useCreateSessionIdMutation();
|
|
@@ -13605,10 +13613,41 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
|
|
|
13605
13613
|
onOfflineWithoutLocalLLM,
|
|
13606
13614
|
});
|
|
13607
13615
|
const [isLoadingChats, setIsLoadingChats] = React.useState(true);
|
|
13616
|
+
// Tracks whether the optional `initialPrompt` prop has already been
|
|
13617
|
+
// auto-submitted for this hook instance. Set to `true` the moment the
|
|
13618
|
+
// effect decides to act (either to send or because the prompt is already
|
|
13619
|
+
// the most recent user message) so subsequent re-renders never re-fire.
|
|
13620
|
+
const initialPromptInjectedRef = React.useRef(false);
|
|
13621
|
+
// Tracks whether a `startNewChat()` call is already mid-flight. The
|
|
13622
|
+
// auto-start `useEffect` below re-runs whenever any of its deps change
|
|
13623
|
+
// (`shouldStartNewChat` in particular toggles after mount in many
|
|
13624
|
+
// consumers), and the existing `!cachedSessionId?.[mentorId]` guard is
|
|
13625
|
+
// not enough on its own because `cachedSessionId` only updates AFTER
|
|
13626
|
+
// `createSessionId` resolves. Without this ref, dep churn during the
|
|
13627
|
+
// pending API call triggers a second `startNewChat`, producing two
|
|
13628
|
+
// sessions, two `getChats` calls, and an extra `setNewMessages([])`
|
|
13629
|
+
// that wipes anything dispatched (like `initialPrompt`'s
|
|
13630
|
+
// `addUserMessage`) between the two `getChats` resolutions.
|
|
13631
|
+
const startNewChatInFlightRef = React.useRef(false);
|
|
13632
|
+
// Synchronous mirror of `isLoadingChats`. The state setter is async, so a
|
|
13633
|
+
// consumer effect running in the SAME render cycle as `getChats()`
|
|
13634
|
+
// invocation sees a stale `isLoadingChats=false` in its closure even
|
|
13635
|
+
// though `setIsLoadingChats(true)` has been scheduled. The ref flips
|
|
13636
|
+
// immediately (before `getChats` hits its first `await`), giving the
|
|
13637
|
+
// `initialPrompt` injection effect a reliable "fetch in progress" signal
|
|
13638
|
+
// it can read synchronously inside its body.
|
|
13639
|
+
const isLoadingChatsRef = React.useRef(true);
|
|
13608
13640
|
const [getSessionChats] = dataLayer.useLazyGetSessionIdQuery();
|
|
13609
13641
|
const [getSharedSessionChats] = dataLayer.useLazyGetSharedSessionIdQuery();
|
|
13610
13642
|
const getChats = React.useCallback(async () => {
|
|
13611
13643
|
var _a, _b, _c, _d;
|
|
13644
|
+
// Mark the fetch as in-flight so consumers gated on `isLoadingChats`
|
|
13645
|
+
// (e.g. the `initialPrompt` injection effect) defer until the resulting
|
|
13646
|
+
// `setNewMessages` has landed. The state setter alone is not enough —
|
|
13647
|
+
// it's async and the consumer effect may run in the same render cycle
|
|
13648
|
+
// with the stale closure — so flip the synchronous ref FIRST.
|
|
13649
|
+
isLoadingChatsRef.current = true;
|
|
13650
|
+
setIsLoadingChats(true);
|
|
13612
13651
|
let data;
|
|
13613
13652
|
try {
|
|
13614
13653
|
if (showingSharedChat) {
|
|
@@ -13693,6 +13732,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
|
|
|
13693
13732
|
}
|
|
13694
13733
|
finally {
|
|
13695
13734
|
setIsLoadingChats(false);
|
|
13735
|
+
isLoadingChatsRef.current = false;
|
|
13696
13736
|
}
|
|
13697
13737
|
}, [
|
|
13698
13738
|
showingSharedChat,
|
|
@@ -13704,21 +13744,30 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
|
|
|
13704
13744
|
getSessionChats,
|
|
13705
13745
|
dispatch,
|
|
13706
13746
|
]);
|
|
13747
|
+
// Depend on the indexed string session id, not the whole `cachedSessionId`
|
|
13748
|
+
// object. Consumers (e.g. `useLocalStorage`) often re-emit a new object
|
|
13749
|
+
// reference on every render even when the underlying value is unchanged,
|
|
13750
|
+
// which would otherwise cause this effect to re-fire `getChats()` per
|
|
13751
|
+
// render — and each `setNewMessages` would wipe any user message
|
|
13752
|
+
// dispatched in between (the URL `initialPrompt` race).
|
|
13753
|
+
const currentMentorSessionId = cachedSessionId === null || cachedSessionId === void 0 ? void 0 : cachedSessionId[mentorId];
|
|
13707
13754
|
React.useEffect(() => {
|
|
13708
|
-
if (
|
|
13709
|
-
dispatch(chatActions.updateSessionIds(
|
|
13755
|
+
if (currentMentorSessionId) {
|
|
13756
|
+
dispatch(chatActions.updateSessionIds(currentMentorSessionId));
|
|
13710
13757
|
// Skip fetching previous chats when offline
|
|
13711
13758
|
if (!isOffline) {
|
|
13712
13759
|
getChats();
|
|
13713
13760
|
}
|
|
13714
13761
|
else {
|
|
13715
13762
|
setIsLoadingChats(false);
|
|
13763
|
+
isLoadingChatsRef.current = false;
|
|
13716
13764
|
}
|
|
13717
13765
|
}
|
|
13718
13766
|
else {
|
|
13719
13767
|
setIsLoadingChats(false);
|
|
13768
|
+
isLoadingChatsRef.current = false;
|
|
13720
13769
|
}
|
|
13721
|
-
}, [
|
|
13770
|
+
}, [currentMentorSessionId, isOffline]);
|
|
13722
13771
|
const startNewChat = React.useCallback(async () => {
|
|
13723
13772
|
// Reset all chat state
|
|
13724
13773
|
if (!showingSharedChat) {
|
|
@@ -13781,10 +13830,15 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
|
|
|
13781
13830
|
errorHandler,
|
|
13782
13831
|
]);
|
|
13783
13832
|
React.useEffect(() => {
|
|
13833
|
+
if (startNewChatInFlightRef.current)
|
|
13834
|
+
return;
|
|
13784
13835
|
if (!showingSharedChat || mentorSettings.allowAnonymous) {
|
|
13785
13836
|
if (mentorSettings.allowAnonymous !== undefined &&
|
|
13786
13837
|
!(cachedSessionId === null || cachedSessionId === void 0 ? void 0 : cachedSessionId[mentorId])) {
|
|
13787
|
-
|
|
13838
|
+
startNewChatInFlightRef.current = true;
|
|
13839
|
+
startNewChat().finally(() => {
|
|
13840
|
+
startNewChatInFlightRef.current = false;
|
|
13841
|
+
});
|
|
13788
13842
|
}
|
|
13789
13843
|
}
|
|
13790
13844
|
}, [shouldStartNewChat, showingSharedChat, mentorSettings.allowAnonymous]);
|
|
@@ -13795,6 +13849,76 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
|
|
|
13795
13849
|
resetConnection();
|
|
13796
13850
|
}
|
|
13797
13851
|
}, [sessionIds, activeTab, sessionId, resetConnection]);
|
|
13852
|
+
// Auto-submit `initialPrompt` exactly once per mount after the chat
|
|
13853
|
+
// lifecycle has stabilized. Matches the existing proactive-prompt
|
|
13854
|
+
// precedent in `changeTab` (sendMessage(tab, content)) but is gated on
|
|
13855
|
+
// URL-driven deep-link signals instead of tab transitions.
|
|
13856
|
+
React.useEffect(() => {
|
|
13857
|
+
var _a;
|
|
13858
|
+
if (initialPromptInjectedRef.current)
|
|
13859
|
+
return;
|
|
13860
|
+
const trimmedPrompt = initialPrompt === null || initialPrompt === void 0 ? void 0 : initialPrompt.trim();
|
|
13861
|
+
if (!trimmedPrompt)
|
|
13862
|
+
return;
|
|
13863
|
+
// Bail while any state would make auto-submission unsafe or premature.
|
|
13864
|
+
if (isPreviewMode)
|
|
13865
|
+
return;
|
|
13866
|
+
if (showingSharedChat)
|
|
13867
|
+
return;
|
|
13868
|
+
if (isOffline)
|
|
13869
|
+
return;
|
|
13870
|
+
// Two-layer guard: the state catches re-runs after `getChats` settles
|
|
13871
|
+
// (which schedule a new render). The ref catches the in-render race
|
|
13872
|
+
// where this effect runs in the same cycle that `getChats` was invoked
|
|
13873
|
+
// — the state setter is async and the closure has a stale `false`.
|
|
13874
|
+
if (isLoadingChats)
|
|
13875
|
+
return;
|
|
13876
|
+
if (isLoadingChatsRef.current)
|
|
13877
|
+
return;
|
|
13878
|
+
if (!sessionIds[activeTab])
|
|
13879
|
+
return;
|
|
13880
|
+
if (streaming)
|
|
13881
|
+
return;
|
|
13882
|
+
if (isPending)
|
|
13883
|
+
return;
|
|
13884
|
+
// Dedup: scan back through the loaded history for the most recent
|
|
13885
|
+
// user message and compare its content. If it matches, this session
|
|
13886
|
+
// already has the prompt as a user turn (typical case: reload of a
|
|
13887
|
+
// `?prompt=` URL — the cached history ends with the assistant reply,
|
|
13888
|
+
// so the matching user message is one step back).
|
|
13889
|
+
const messages = chats[activeTab];
|
|
13890
|
+
let lastUserMessage;
|
|
13891
|
+
if (messages && messages.length > 0) {
|
|
13892
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
13893
|
+
if (((_a = messages[i]) === null || _a === void 0 ? void 0 : _a.role) === "user") {
|
|
13894
|
+
lastUserMessage = messages[i];
|
|
13895
|
+
break;
|
|
13896
|
+
}
|
|
13897
|
+
}
|
|
13898
|
+
}
|
|
13899
|
+
if (lastUserMessage &&
|
|
13900
|
+
typeof lastUserMessage.content === "string" &&
|
|
13901
|
+
lastUserMessage.content === trimmedPrompt) {
|
|
13902
|
+
initialPromptInjectedRef.current = true;
|
|
13903
|
+
return;
|
|
13904
|
+
}
|
|
13905
|
+
// Mark BEFORE dispatching: sendMessage triggers state updates that may
|
|
13906
|
+
// re-run this effect, and we want the guard active before any re-fire.
|
|
13907
|
+
initialPromptInjectedRef.current = true;
|
|
13908
|
+
sendMessage(activeTab, trimmedPrompt);
|
|
13909
|
+
}, [
|
|
13910
|
+
initialPrompt,
|
|
13911
|
+
isPreviewMode,
|
|
13912
|
+
showingSharedChat,
|
|
13913
|
+
isOffline,
|
|
13914
|
+
isLoadingChats,
|
|
13915
|
+
sessionIds,
|
|
13916
|
+
activeTab,
|
|
13917
|
+
chats,
|
|
13918
|
+
streaming,
|
|
13919
|
+
isPending,
|
|
13920
|
+
sendMessage,
|
|
13921
|
+
]);
|
|
13798
13922
|
async function changeTab(tab) {
|
|
13799
13923
|
var _a, _b, _c, _d;
|
|
13800
13924
|
// while responding to a message, do not change the tab
|