@iblai/web-utils 1.11.2 → 1.11.4

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.
@@ -0,0 +1,3 @@
1
+ export declare const CHAT_HISTORY_PAGE_SIZE = 10;
2
+ export declare const CHAT_HISTORY_PAGE_PARAM = "page";
3
+ export declare const CHAT_HISTORY_PAGE_SIZE_PARAM = "page_size";
@@ -0,0 +1,9 @@
1
+ export type GetSessionIdArgs = {
2
+ org: string;
3
+ sessionId: string;
4
+ userId?: string;
5
+ page?: number;
6
+ pageSize?: number;
7
+ };
8
+ export declare const buildSessionRetrieveUrl: ({ org, sessionId, userId, page, pageSize, }: GetSessionIdArgs) => string;
9
+ export declare const buildSharedSessionRetrieveUrl: ({ org, sessionId, userId, page, pageSize, }: GetSessionIdArgs) => string;
@@ -50,6 +50,13 @@ export declare const buildEndpointFromDmService: <Args extends Record<string, un
50
50
  export declare const buildEndpointFromAxdService: <Args extends Record<string, unknown>, Result>(serviceFn: (args: Args) => Promise<Result>, options?: BuildEndpointOptions) => {
51
51
  queryFn: BaseQueryFn<Args, Result, FetchBaseQueryError | CustomError>;
52
52
  };
53
+ /**
54
+ * Build an endpoint from a hand-assembled URL (rather than a generated service
55
+ * method), routed through `iblFetchBaseQuery`. Defaults to the DM service.
56
+ */
57
+ export declare const buildEndpointFromDmUrl: <Args extends Record<string, unknown>, Result>(buildUrl: (args: Args) => string, service?: SERVICES) => {
58
+ queryFn: BaseQueryFn<Args, Result, FetchBaseQueryError | CustomError>;
59
+ };
53
60
  export interface CustomQueryArgs extends Omit<FetchArgs, 'url'> {
54
61
  url: string;
55
62
  service: SERVICES;
package/dist/index.d.ts CHANGED
@@ -1101,7 +1101,7 @@ declare const isNode: () => string | false;
1101
1101
  declare const isExpo: () => any;
1102
1102
  declare const isTauri: () => boolean;
1103
1103
  declare function isSafariBrowser(): boolean;
1104
- declare const getPlatform: () => "react-native" | "web" | "node" | "unknown";
1104
+ declare const getPlatform: () => "web" | "react-native" | "node" | "unknown";
1105
1105
  declare const safeRequire: (moduleName: string) => any;
1106
1106
  declare const getNextNavigation: () => any;
1107
1107
 
@@ -1587,6 +1587,9 @@ declare function useAdvancedChat({ tenantKey, mentorId, username, token, wsUrl,
1587
1587
  resetConnection: () => void;
1588
1588
  isLoadingChats: boolean;
1589
1589
  refetchChats: () => Promise<void>;
1590
+ loadOlderMessages: () => Promise<void>;
1591
+ hasMore: boolean;
1592
+ isLoadingOlderMessages: boolean;
1590
1593
  };
1591
1594
 
1592
1595
  type Props$6 = {
package/dist/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import React__default, { useRef, useEffect, useState, useLayoutEffect, useCallback, createContext, useContext, useMemo } from 'react';
3
- import { useRenewSubscriptionMutation, useLazyGetUserAppsQuery, useCreateStripeCustomerPortalMutation, useLazyGetFreeUsageCountQuery, useGetTenantMetadataQuery, useLazyGetStripeContextQuery, useUpdateUserTrialStatusMutation, useLazyGetAccountBillingInfoQuery, useLazyRefreshJwtTokenQuery, useLazyGetUserTenantsQuery, useJoinTenantMutation, useLazyGetTenantMetadataQuery, useGetAppTokensMutation, useGetCustomDomainsQuery, useGetRbacPermissionsMutation, useLazyGetMentorsQuery, useLazySeedMentorsQuery, useLazyGetMentorPublicSettingsQuery, useLazyGetRecentlyAccessedMentorsQuery, useGetMentorSettingsQuery, useGetMentorPublicSettingsQuery, useGetMemsearchStatusQuery, useCreateSessionIdMutation, useLazyGetSessionIdQuery, useLazyGetSharedSessionIdQuery, useEditSessionMutation, useGetToolsQuery, useGetPromptsSearchQuery, useUpdateUserAccountMutation, useGetUserMetadataQuery, useUploadProfileImageMutation, useRemoveProfileImageMutation, useLazyGetStripePricingPageSessionQuery, useGetFileUploadUrlMutation, useGetDisclaimersQuery, useAgreeToDisclaimerMutation, useLazyGetGuidedPromptsQuery, useAudioToTextMutation } from '@iblai/data-layer';
3
+ import { useRenewSubscriptionMutation, useLazyGetUserAppsQuery, useCreateStripeCustomerPortalMutation, useLazyGetFreeUsageCountQuery, useGetTenantMetadataQuery, useLazyGetStripeContextQuery, useUpdateUserTrialStatusMutation, useLazyGetAccountBillingInfoQuery, useLazyRefreshJwtTokenQuery, useLazyGetUserTenantsQuery, useJoinTenantMutation, useLazyGetTenantMetadataQuery, useGetAppTokensMutation, useGetCustomDomainsQuery, useGetRbacPermissionsMutation, useLazyGetMentorsQuery, useLazySeedMentorsQuery, useLazyGetMentorPublicSettingsQuery, useLazyGetRecentlyAccessedMentorsQuery, useLazyGetSessionIdQuery, useLazyGetSharedSessionIdQuery, CHAT_HISTORY_PAGE_SIZE, useGetMentorSettingsQuery, useGetMentorPublicSettingsQuery, useGetMemsearchStatusQuery, useCreateSessionIdMutation, useEditSessionMutation, useGetToolsQuery, useGetPromptsSearchQuery, useUpdateUserAccountMutation, useGetUserMetadataQuery, useUploadProfileImageMutation, useRemoveProfileImageMutation, useLazyGetStripePricingPageSessionQuery, useGetFileUploadUrlMutation, useGetDisclaimersQuery, useAgreeToDisclaimerMutation, useLazyGetGuidedPromptsQuery, useAudioToTextMutation } from '@iblai/data-layer';
4
4
  import { toast } from 'sonner';
5
5
  import { platform } from '@tauri-apps/plugin-os';
6
6
  import { jsx, Fragment } from 'react/jsx-runtime';
@@ -947,9 +947,9 @@ const BASE_CONFIG = {
947
947
  },
948
948
  {
949
949
  slug: "overall_default_mentor",
950
- label: "Default Mentor",
950
+ label: "Default Agent",
951
951
  defaultValue: "",
952
- description: "Default mentor to open on the platform",
952
+ description: "Default agent to open on the platform",
953
953
  type: "string",
954
954
  },
955
955
  /* {
@@ -1087,9 +1087,9 @@ const MENTOR_AI_CONFIG = {
1087
1087
  },
1088
1088
  {
1089
1089
  slug: "mentor_include_community_mentors",
1090
- label: "Community Mentors",
1090
+ label: "Community Agents",
1091
1091
  defaultValue: true,
1092
- description: "Allow users to access the mentors from the community.",
1092
+ description: "Allow users to access the agents from the community.",
1093
1093
  type: "boolean",
1094
1094
  },
1095
1095
  {
@@ -11295,6 +11295,23 @@ const chatSlice = createSlice({
11295
11295
  [state.activeTab]: action.payload,
11296
11296
  };
11297
11297
  },
11298
+ prependOlderMessages: (state, action) => {
11299
+ var _a;
11300
+ const olderMessages = action.payload;
11301
+ if (!olderMessages || olderMessages.length === 0) {
11302
+ return;
11303
+ }
11304
+ const existing = (_a = state.chats[state.activeTab]) !== null && _a !== void 0 ? _a : [];
11305
+ const existingIds = new Set(existing.map((message) => message.id));
11306
+ const deduped = olderMessages.filter((message) => !existingIds.has(message.id));
11307
+ if (deduped.length === 0) {
11308
+ return;
11309
+ }
11310
+ state.chats = {
11311
+ ...state.chats,
11312
+ [state.activeTab]: [...deduped, ...existing],
11313
+ };
11314
+ },
11298
11315
  resetChats: (state) => {
11299
11316
  state.chats = defaultChatState;
11300
11317
  },
@@ -11343,7 +11360,7 @@ const chatSlice = createSlice({
11343
11360
  }
11344
11361
  },
11345
11362
  // Force-flush buffered reasoning to currentStreamingMessage
11346
- flushReasoningBuffer: (state, _action) => {
11363
+ flushReasoningBuffer: (state) => {
11347
11364
  if (state.streamingReasoningContentBuffer.length > 0) {
11348
11365
  state.currentStreamingMessage.reasoningContent +=
11349
11366
  state.streamingReasoningContentBuffer;
@@ -11356,7 +11373,7 @@ const chatSlice = createSlice({
11356
11373
  state.currentStreamingMessage.isReasoning = action.payload;
11357
11374
  },
11358
11375
  // Sets isReasoning = false (triggers UI collapse)
11359
- setReasoningComplete: (state, _action) => {
11376
+ setReasoningComplete: (state) => {
11360
11377
  state.currentStreamingMessage.isReasoning = false;
11361
11378
  },
11362
11379
  // Appends tool call entry
@@ -11503,7 +11520,7 @@ const chatSlice = createSlice({
11503
11520
  }
11504
11521
  },
11505
11522
  // Force flush any buffered content (called on artifact end or when UI needs update)
11506
- flushStreamingArtifactBuffer: (state, _action) => {
11523
+ flushStreamingArtifactBuffer: (state) => {
11507
11524
  if (state.currentStreamingArtifact &&
11508
11525
  state.streamingArtifactContentBuffer.length > 0) {
11509
11526
  state.currentStreamingArtifact.content +=
@@ -11512,7 +11529,7 @@ const chatSlice = createSlice({
11512
11529
  state.lastArtifactContentFlushTime = Date.now();
11513
11530
  }
11514
11531
  },
11515
- ensureStreamingAssistantMessage: (state, _action) => {
11532
+ ensureStreamingAssistantMessage: (state) => {
11516
11533
  const streamingId = state.currentStreamingMessage.id;
11517
11534
  if (!streamingId) {
11518
11535
  return;
@@ -11652,7 +11669,7 @@ const chatSlice = createSlice({
11652
11669
  action.payload.fullContent || state.currentStreamingArtifact.content;
11653
11670
  }
11654
11671
  },
11655
- clearStreamingArtifact: (state, _action) => {
11672
+ clearStreamingArtifact: (state) => {
11656
11673
  // Clear buffer along with artifact
11657
11674
  state.streamingArtifactContentBuffer = "";
11658
11675
  state.lastArtifactContentFlushTime = 0;
@@ -13883,6 +13900,281 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
13883
13900
  };
13884
13901
  };
13885
13902
 
13903
+ const INITIAL_CHAT_PAGE = 1;
13904
+ function normalizeSessionResult(result) {
13905
+ var _a, _b, _c, _d, _e, _f;
13906
+ const artifactVersions = (_a = result.artifact_versions) === null || _a === void 0 ? void 0 : _a.map((av) => {
13907
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
13908
+ return ({
13909
+ id: av.id,
13910
+ artifact: {
13911
+ id: (_a = av.artifact) === null || _a === void 0 ? void 0 : _a.id,
13912
+ title: (_b = av.artifact) === null || _b === void 0 ? void 0 : _b.title,
13913
+ content: (_c = av.artifact) === null || _c === void 0 ? void 0 : _c.content,
13914
+ file_extension: (_d = av.artifact) === null || _d === void 0 ? void 0 : _d.file_extension,
13915
+ llm_name: (_e = av.artifact) === null || _e === void 0 ? void 0 : _e.llm_name,
13916
+ llm_provider: (_f = av.artifact) === null || _f === void 0 ? void 0 : _f.llm_provider,
13917
+ date_created: (_g = av.artifact) === null || _g === void 0 ? void 0 : _g.date_created,
13918
+ date_updated: (_h = av.artifact) === null || _h === void 0 ? void 0 : _h.date_updated,
13919
+ metadata: (_j = av.artifact) === null || _j === void 0 ? void 0 : _j.metadata,
13920
+ username: (_k = av.artifact) === null || _k === void 0 ? void 0 : _k.username,
13921
+ session_id: (_l = av.artifact) === null || _l === void 0 ? void 0 : _l.session_id,
13922
+ current_version_number: (_m = av.artifact) === null || _m === void 0 ? void 0 : _m.current_version_number,
13923
+ version_count: (_o = av.artifact) === null || _o === void 0 ? void 0 : _o.version_count,
13924
+ },
13925
+ title: av.title,
13926
+ content: av.content,
13927
+ session_id: av.session_id,
13928
+ content_length: av.content_length,
13929
+ is_current: av.is_current,
13930
+ chat_message: av.chat_message,
13931
+ version_number: av.version_number,
13932
+ date_created: av.date_created,
13933
+ created_by: av.created_by,
13934
+ change_summary: av.change_summary,
13935
+ });
13936
+ });
13937
+ const fileAttachments = ((_b = result.files) === null || _b === void 0 ? void 0 : _b.map((file) => ({
13938
+ fileId: String(file.id),
13939
+ fileName: file.name,
13940
+ fileType: file.content_type,
13941
+ fileSize: file.file_size,
13942
+ uploadUrl: file.url,
13943
+ }))) || [];
13944
+ // Extract additional_kwargs - check both direct and nested paths
13945
+ const additionalKwargs = result.additional_kwargs || ((_d = (_c = result.message) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.additional_kwargs);
13946
+ // Extract reasoning content from additional_kwargs
13947
+ const reasoningSummaries = (_e = additionalKwargs === null || additionalKwargs === void 0 ? void 0 : additionalKwargs.reasoning) === null || _e === void 0 ? void 0 : _e.summary;
13948
+ const reasoningContent = Array.isArray(reasoningSummaries)
13949
+ ? reasoningSummaries
13950
+ .map((s) => s.text)
13951
+ .filter(Boolean)
13952
+ .join("\n\n")
13953
+ : undefined;
13954
+ // Extract tool calls from additional_kwargs
13955
+ const toolOutputs = additionalKwargs === null || additionalKwargs === void 0 ? void 0 : additionalKwargs.tool_outputs;
13956
+ const toolCalls = Array.isArray(toolOutputs)
13957
+ ? toolOutputs.map((tool) => {
13958
+ var _a;
13959
+ return ({
13960
+ id: tool.id || "",
13961
+ name: tool.type || "",
13962
+ log: ((_a = tool.action) === null || _a === void 0 ? void 0 : _a.query) || "",
13963
+ result: tool.status || "",
13964
+ });
13965
+ })
13966
+ : undefined;
13967
+ return {
13968
+ ...result,
13969
+ role: result.type === "human" ? "user" : "assistant",
13970
+ visible: true,
13971
+ id: result.id || new Date().getTime(),
13972
+ content: typeof result.content === "object" && result.content.length > 0
13973
+ ? (_f = result.content.find((item) => item.text)) === null || _f === void 0 ? void 0 : _f.text
13974
+ : result.content,
13975
+ // Add transformed artifact versions
13976
+ artifactVersions: artifactVersions || undefined,
13977
+ fileAttachments: fileAttachments.length > 0 ? fileAttachments : undefined,
13978
+ reasoningContent: reasoningContent || undefined,
13979
+ toolCalls: toolCalls && toolCalls.length > 0 ? toolCalls : undefined,
13980
+ };
13981
+ }
13982
+ function normalizeSessionResults(results) {
13983
+ return (results === null || results === void 0 ? void 0 : results.map(normalizeSessionResult).reverse()) || [];
13984
+ }
13985
+ /**
13986
+ * A session re-fetch can omit `additional_kwargs`, dropping reasoning/tool-call
13987
+ * data that is already in memory. Carry those fields forward from the matching
13988
+ * existing message (by content) onto the freshly-fetched message in place.
13989
+ */
13990
+ function mergeReasoningAndToolCalls(incoming, existing) {
13991
+ if (existing.length === 0 || incoming.length === 0)
13992
+ return;
13993
+ const existingByContent = new Map();
13994
+ for (const m of existing) {
13995
+ if (m.reasoningContent || (m.toolCalls && m.toolCalls.length > 0)) {
13996
+ existingByContent.set(m.content, m);
13997
+ }
13998
+ }
13999
+ if (existingByContent.size === 0)
14000
+ return;
14001
+ for (const msg of incoming) {
14002
+ const match = existingByContent.get(msg.content);
14003
+ if (!match)
14004
+ continue;
14005
+ if (!msg.reasoningContent && match.reasoningContent) {
14006
+ msg.reasoningContent = match.reasoningContent;
14007
+ }
14008
+ if ((!msg.toolCalls || msg.toolCalls.length === 0) &&
14009
+ match.toolCalls &&
14010
+ match.toolCalls.length > 0) {
14011
+ msg.toolCalls = match.toolCalls;
14012
+ }
14013
+ }
14014
+ }
14015
+
14016
+ function useChatHistory({ tenantKey, mentorId, username, cachedSessionId, isOffline = false, }) {
14017
+ const dispatch = useDispatch();
14018
+ const chats = useSelector(selectChats);
14019
+ const activeTab = useSelector(selectActiveTab);
14020
+ const showingSharedChat = useSelector(selectShowingSharedChat);
14021
+ const [isLoadingChats, setIsLoadingChats] = useState(true);
14022
+ // Synchronous mirror of `isLoadingChats`. The state setter is async, so a
14023
+ // consumer effect running in the SAME render cycle as `getChats()`
14024
+ // invocation sees a stale `isLoadingChats=false` in its closure even
14025
+ // though `setIsLoadingChats(true)` has been scheduled. The ref flips
14026
+ // immediately (before `getChats` hits its first `await`), giving the
14027
+ // `initialPrompt` injection effect a reliable "fetch in progress" signal
14028
+ // it can read synchronously inside its body.
14029
+ const isLoadingChatsRef = React__default.useRef(true);
14030
+ const [hasMore, setHasMore] = useState(false);
14031
+ const [isLoadingOlderMessages, setIsLoadingOlderMessages] = useState(false);
14032
+ const currentPageRef = React__default.useRef(INITIAL_CHAT_PAGE);
14033
+ const isLoadingOlderRef = React__default.useRef(false);
14034
+ const [getSessionChats] = useLazyGetSessionIdQuery();
14035
+ const [getSharedSessionChats] = useLazyGetSharedSessionIdQuery();
14036
+ const fetchChatPage = React__default.useCallback((sessionId, page) => {
14037
+ if (showingSharedChat) {
14038
+ return getSharedSessionChats({
14039
+ sessionId,
14040
+ org: tenantKey,
14041
+ page,
14042
+ pageSize: CHAT_HISTORY_PAGE_SIZE,
14043
+ });
14044
+ }
14045
+ if (!username || username === ANONYMOUS_USERNAME) {
14046
+ return undefined;
14047
+ }
14048
+ return getSessionChats({
14049
+ sessionId,
14050
+ org: tenantKey,
14051
+ userId: username,
14052
+ page,
14053
+ pageSize: CHAT_HISTORY_PAGE_SIZE,
14054
+ });
14055
+ }, [
14056
+ showingSharedChat,
14057
+ getSharedSessionChats,
14058
+ getSessionChats,
14059
+ tenantKey,
14060
+ username,
14061
+ ]);
14062
+ const getChats = React__default.useCallback(async () => {
14063
+ var _a, _b, _c, _d;
14064
+ // Mark the fetch as in-flight so consumers gated on `isLoadingChats`
14065
+ // (e.g. the `initialPrompt` injection effect) defer until the resulting
14066
+ // `setNewMessages` has landed. The state setter alone is not enough —
14067
+ // it's async and the consumer effect may run in the same render cycle
14068
+ // with the stale closure — so flip the synchronous ref FIRST.
14069
+ isLoadingChatsRef.current = true;
14070
+ setIsLoadingChats(true);
14071
+ let data;
14072
+ try {
14073
+ data = await fetchChatPage((_a = cachedSessionId === null || cachedSessionId === void 0 ? void 0 : cachedSessionId[mentorId]) !== null && _a !== void 0 ? _a : "", INITIAL_CHAT_PAGE);
14074
+ let previousChats = [];
14075
+ try {
14076
+ previousChats = normalizeSessionResults((_b = data === null || data === void 0 ? void 0 : data.data) === null || _b === void 0 ? void 0 : _b.results);
14077
+ }
14078
+ catch (error) {
14079
+ console.error(JSON.stringify(error));
14080
+ }
14081
+ const nextPage = (_d = (_c = data === null || data === void 0 ? void 0 : data.data) === null || _c === void 0 ? void 0 : _c.next) !== null && _d !== void 0 ? _d : null;
14082
+ currentPageRef.current = INITIAL_CHAT_PAGE;
14083
+ setHasMore(nextPage !== null);
14084
+ mergeReasoningAndToolCalls(previousChats, chats[activeTab] || []);
14085
+ dispatch(chatActions.setNewMessages(previousChats));
14086
+ }
14087
+ catch (error) {
14088
+ console.error(JSON.stringify(error));
14089
+ }
14090
+ finally {
14091
+ setIsLoadingChats(false);
14092
+ isLoadingChatsRef.current = false;
14093
+ }
14094
+ }, [fetchChatPage, cachedSessionId, mentorId, dispatch]);
14095
+ const loadOlderMessages = React__default.useCallback(async () => {
14096
+ var _a, _b, _c;
14097
+ if (isLoadingOlderRef.current)
14098
+ return;
14099
+ if (!hasMore)
14100
+ return;
14101
+ if (isOffline)
14102
+ return;
14103
+ if (!showingSharedChat && (!username || username === ANONYMOUS_USERNAME)) {
14104
+ return;
14105
+ }
14106
+ const activeSessionId = cachedSessionId === null || cachedSessionId === void 0 ? void 0 : cachedSessionId[mentorId];
14107
+ if (!activeSessionId)
14108
+ return;
14109
+ isLoadingOlderRef.current = true;
14110
+ setIsLoadingOlderMessages(true);
14111
+ const nextPage = currentPageRef.current + 1;
14112
+ try {
14113
+ const data = await fetchChatPage(activeSessionId, nextPage);
14114
+ let olderChats = [];
14115
+ try {
14116
+ olderChats = normalizeSessionResults((_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.results);
14117
+ }
14118
+ catch (error) {
14119
+ console.error(JSON.stringify(error));
14120
+ }
14121
+ if (olderChats.length > 0) {
14122
+ dispatch(chatActions.prependOlderMessages(olderChats));
14123
+ }
14124
+ currentPageRef.current = nextPage;
14125
+ setHasMore(((_c = (_b = data === null || data === void 0 ? void 0 : data.data) === null || _b === void 0 ? void 0 : _b.next) !== null && _c !== void 0 ? _c : null) !== null);
14126
+ }
14127
+ catch (error) {
14128
+ console.error(JSON.stringify(error));
14129
+ }
14130
+ finally {
14131
+ isLoadingOlderRef.current = false;
14132
+ setIsLoadingOlderMessages(false);
14133
+ }
14134
+ }, [
14135
+ hasMore,
14136
+ isOffline,
14137
+ showingSharedChat,
14138
+ username,
14139
+ cachedSessionId,
14140
+ mentorId,
14141
+ fetchChatPage,
14142
+ dispatch,
14143
+ ]);
14144
+ // Depend on the indexed string session id, not the whole `cachedSessionId`
14145
+ // object. Consumers (e.g. `useLocalStorage`) often re-emit a new object
14146
+ // reference on every render even when the underlying value is unchanged,
14147
+ // which would otherwise cause this effect to re-fire `getChats()` per
14148
+ // render — and each `setNewMessages` would wipe any user message
14149
+ // dispatched in between (the URL `initialPrompt` race).
14150
+ const currentMentorSessionId = cachedSessionId === null || cachedSessionId === void 0 ? void 0 : cachedSessionId[mentorId];
14151
+ useEffect(() => {
14152
+ if (currentMentorSessionId) {
14153
+ dispatch(chatActions.updateSessionIds(currentMentorSessionId));
14154
+ // Skip fetching previous chats when offline
14155
+ if (!isOffline) {
14156
+ getChats();
14157
+ }
14158
+ else {
14159
+ setIsLoadingChats(false);
14160
+ isLoadingChatsRef.current = false;
14161
+ }
14162
+ }
14163
+ else {
14164
+ setIsLoadingChats(false);
14165
+ isLoadingChatsRef.current = false;
14166
+ }
14167
+ }, [currentMentorSessionId, isOffline]);
14168
+ return {
14169
+ getChats,
14170
+ loadOlderMessages,
14171
+ hasMore,
14172
+ isLoadingOlderMessages,
14173
+ isLoadingChats,
14174
+ isLoadingChatsRef,
14175
+ };
14176
+ }
14177
+
13886
14178
  function useMentorSettings({ mentorId, tenantKey, isPublicRoute = false, mainTenantKey, }) {
13887
14179
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
13888
14180
  const username = useUsername();
@@ -14058,7 +14350,13 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
14058
14350
  isOffline,
14059
14351
  onOfflineWithoutLocalLLM,
14060
14352
  });
14061
- const [isLoadingChats, setIsLoadingChats] = useState(true);
14353
+ const { getChats, loadOlderMessages, hasMore, isLoadingOlderMessages, isLoadingChats, isLoadingChatsRef, } = useChatHistory({
14354
+ tenantKey,
14355
+ mentorId,
14356
+ username,
14357
+ cachedSessionId,
14358
+ isOffline,
14359
+ });
14062
14360
  // Tracks whether the optional `initialPrompt` prop has already been
14063
14361
  // auto-submitted for this hook instance. Set to `true` the moment the
14064
14362
  // effect decides to act (either to send or because the prompt is already
@@ -14075,197 +14373,6 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
14075
14373
  // that wipes anything dispatched (like `initialPrompt`'s
14076
14374
  // `addUserMessage`) between the two `getChats` resolutions.
14077
14375
  const startNewChatInFlightRef = React__default.useRef(false);
14078
- // Synchronous mirror of `isLoadingChats`. The state setter is async, so a
14079
- // consumer effect running in the SAME render cycle as `getChats()`
14080
- // invocation sees a stale `isLoadingChats=false` in its closure even
14081
- // though `setIsLoadingChats(true)` has been scheduled. The ref flips
14082
- // immediately (before `getChats` hits its first `await`), giving the
14083
- // `initialPrompt` injection effect a reliable "fetch in progress" signal
14084
- // it can read synchronously inside its body.
14085
- const isLoadingChatsRef = React__default.useRef(true);
14086
- const [getSessionChats] = useLazyGetSessionIdQuery();
14087
- const [getSharedSessionChats] = useLazyGetSharedSessionIdQuery();
14088
- const getChats = React__default.useCallback(async () => {
14089
- var _a, _b, _c, _d;
14090
- // Mark the fetch as in-flight so consumers gated on `isLoadingChats`
14091
- // (e.g. the `initialPrompt` injection effect) defer until the resulting
14092
- // `setNewMessages` has landed. The state setter alone is not enough —
14093
- // it's async and the consumer effect may run in the same render cycle
14094
- // with the stale closure — so flip the synchronous ref FIRST.
14095
- isLoadingChatsRef.current = true;
14096
- setIsLoadingChats(true);
14097
- let data;
14098
- try {
14099
- if (showingSharedChat) {
14100
- data = await getSharedSessionChats({
14101
- sessionId: (_a = cachedSessionId === null || cachedSessionId === void 0 ? void 0 : cachedSessionId[mentorId]) !== null && _a !== void 0 ? _a : "",
14102
- org: tenantKey,
14103
- });
14104
- }
14105
- else if (!!username && username !== ANONYMOUS_USERNAME) {
14106
- data = await getSessionChats({
14107
- sessionId: (_b = cachedSessionId === null || cachedSessionId === void 0 ? void 0 : cachedSessionId[mentorId]) !== null && _b !== void 0 ? _b : "",
14108
- org: tenantKey,
14109
- // @ts-expect-error - userId is passed but not in generated API types
14110
- userId: username,
14111
- });
14112
- }
14113
- let previousChats = [];
14114
- try {
14115
- previousChats =
14116
- ((_d = (_c = data === null || data === void 0 ? void 0 : data.data) === null || _c === void 0 ? void 0 : _c.results) === null || _d === void 0 ? void 0 : _d.map((result) => {
14117
- var _a, _b, _c, _d, _e, _f;
14118
- const artifactVersions = (_a = result.artifact_versions) === null || _a === void 0 ? void 0 : _a.map((av) => {
14119
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
14120
- return ({
14121
- id: av.id,
14122
- artifact: {
14123
- id: (_a = av.artifact) === null || _a === void 0 ? void 0 : _a.id,
14124
- title: (_b = av.artifact) === null || _b === void 0 ? void 0 : _b.title,
14125
- content: (_c = av.artifact) === null || _c === void 0 ? void 0 : _c.content,
14126
- file_extension: (_d = av.artifact) === null || _d === void 0 ? void 0 : _d.file_extension,
14127
- llm_name: (_e = av.artifact) === null || _e === void 0 ? void 0 : _e.llm_name,
14128
- llm_provider: (_f = av.artifact) === null || _f === void 0 ? void 0 : _f.llm_provider,
14129
- date_created: (_g = av.artifact) === null || _g === void 0 ? void 0 : _g.date_created,
14130
- date_updated: (_h = av.artifact) === null || _h === void 0 ? void 0 : _h.date_updated,
14131
- metadata: (_j = av.artifact) === null || _j === void 0 ? void 0 : _j.metadata,
14132
- username: (_k = av.artifact) === null || _k === void 0 ? void 0 : _k.username,
14133
- session_id: (_l = av.artifact) === null || _l === void 0 ? void 0 : _l.session_id,
14134
- current_version_number: (_m = av.artifact) === null || _m === void 0 ? void 0 : _m.current_version_number,
14135
- version_count: (_o = av.artifact) === null || _o === void 0 ? void 0 : _o.version_count,
14136
- },
14137
- title: av.title,
14138
- content: av.content,
14139
- session_id: av.session_id,
14140
- content_length: av.content_length,
14141
- is_current: av.is_current,
14142
- chat_message: av.chat_message,
14143
- version_number: av.version_number,
14144
- date_created: av.date_created,
14145
- created_by: av.created_by,
14146
- change_summary: av.change_summary,
14147
- });
14148
- });
14149
- const fileAttachments = ((_b = result.files) === null || _b === void 0 ? void 0 : _b.map((file) => ({
14150
- fileId: String(file.id),
14151
- fileName: file.name,
14152
- fileType: file.content_type,
14153
- fileSize: file.file_size,
14154
- uploadUrl: file.url,
14155
- }))) || [];
14156
- // Extract additional_kwargs - check both direct and nested paths
14157
- const additionalKwargs = result.additional_kwargs ||
14158
- ((_d = (_c = result.message) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.additional_kwargs);
14159
- // Extract reasoning content from additional_kwargs
14160
- const reasoningSummaries = (_e = additionalKwargs === null || additionalKwargs === void 0 ? void 0 : additionalKwargs.reasoning) === null || _e === void 0 ? void 0 : _e.summary;
14161
- const reasoningContent = Array.isArray(reasoningSummaries)
14162
- ? reasoningSummaries
14163
- .map((s) => s.text)
14164
- .filter(Boolean)
14165
- .join("\n\n")
14166
- : undefined;
14167
- // Extract tool calls from additional_kwargs
14168
- const toolOutputs = additionalKwargs === null || additionalKwargs === void 0 ? void 0 : additionalKwargs.tool_outputs;
14169
- const toolCalls = Array.isArray(toolOutputs)
14170
- ? toolOutputs.map((tool) => {
14171
- var _a;
14172
- return ({
14173
- id: tool.id || "",
14174
- name: tool.type || "",
14175
- log: ((_a = tool.action) === null || _a === void 0 ? void 0 : _a.query) || "",
14176
- result: tool.status || "",
14177
- });
14178
- })
14179
- : undefined;
14180
- return {
14181
- ...result,
14182
- role: result.type === "human" ? "user" : "assistant",
14183
- visible: true,
14184
- id: result.id || new Date().getTime(),
14185
- content: typeof result.content === "object" &&
14186
- result.content.length > 0
14187
- ? (_f = result.content.find((item) => item.text)) === null || _f === void 0 ? void 0 : _f.text
14188
- : result.content,
14189
- // Add transformed artifact versions
14190
- artifactVersions: artifactVersions || undefined,
14191
- fileAttachments: fileAttachments.length > 0 ? fileAttachments : undefined,
14192
- reasoningContent: reasoningContent || undefined,
14193
- toolCalls: toolCalls && toolCalls.length > 0 ? toolCalls : undefined,
14194
- };
14195
- }).reverse()) || [];
14196
- }
14197
- catch (error) {
14198
- console.error(JSON.stringify(error));
14199
- }
14200
- // Preserve reasoningContent and toolCalls from existing messages
14201
- // when the session API re-fetch doesn't include additional_kwargs.
14202
- const existingMessages = chats[activeTab] || [];
14203
- if (existingMessages.length > 0 && previousChats.length > 0) {
14204
- const existingByContent = new Map();
14205
- for (const m of existingMessages) {
14206
- if (m.reasoningContent || (m.toolCalls && m.toolCalls.length > 0)) {
14207
- existingByContent.set(m.content, m);
14208
- }
14209
- }
14210
- if (existingByContent.size > 0) {
14211
- for (const msg of previousChats) {
14212
- const existing = existingByContent.get(msg.content);
14213
- if (existing) {
14214
- if (!msg.reasoningContent && existing.reasoningContent) {
14215
- msg.reasoningContent = existing.reasoningContent;
14216
- }
14217
- if ((!msg.toolCalls || msg.toolCalls.length === 0) &&
14218
- existing.toolCalls &&
14219
- existing.toolCalls.length > 0) {
14220
- msg.toolCalls = existing.toolCalls;
14221
- }
14222
- }
14223
- }
14224
- }
14225
- }
14226
- dispatch(chatActions.setNewMessages(previousChats));
14227
- }
14228
- catch (error) {
14229
- console.error(JSON.stringify(error));
14230
- }
14231
- finally {
14232
- setIsLoadingChats(false);
14233
- isLoadingChatsRef.current = false;
14234
- }
14235
- }, [
14236
- showingSharedChat,
14237
- cachedSessionId,
14238
- mentorId,
14239
- tenantKey,
14240
- username,
14241
- getSharedSessionChats,
14242
- getSessionChats,
14243
- dispatch,
14244
- ]);
14245
- // Depend on the indexed string session id, not the whole `cachedSessionId`
14246
- // object. Consumers (e.g. `useLocalStorage`) often re-emit a new object
14247
- // reference on every render even when the underlying value is unchanged,
14248
- // which would otherwise cause this effect to re-fire `getChats()` per
14249
- // render — and each `setNewMessages` would wipe any user message
14250
- // dispatched in between (the URL `initialPrompt` race).
14251
- const currentMentorSessionId = cachedSessionId === null || cachedSessionId === void 0 ? void 0 : cachedSessionId[mentorId];
14252
- useEffect(() => {
14253
- if (currentMentorSessionId) {
14254
- dispatch(chatActions.updateSessionIds(currentMentorSessionId));
14255
- // Skip fetching previous chats when offline
14256
- if (!isOffline) {
14257
- getChats();
14258
- }
14259
- else {
14260
- setIsLoadingChats(false);
14261
- isLoadingChatsRef.current = false;
14262
- }
14263
- }
14264
- else {
14265
- setIsLoadingChats(false);
14266
- isLoadingChatsRef.current = false;
14267
- }
14268
- }, [currentMentorSessionId, isOffline]);
14269
14376
  const startNewChat = React__default.useCallback(async () => {
14270
14377
  // Reset all chat state
14271
14378
  if (!showingSharedChat) {
@@ -14498,6 +14605,9 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
14498
14605
  resetConnection,
14499
14606
  isLoadingChats,
14500
14607
  refetchChats: getChats,
14608
+ loadOlderMessages,
14609
+ hasMore,
14610
+ isLoadingOlderMessages,
14501
14611
  };
14502
14612
  }
14503
14613