buster/apps/web/src/api/buster-electric/messages/hooks.ts

205 lines
7.1 KiB
TypeScript
Raw Normal View History

import type { ChatMessageResponseMessage_File } from '@buster/server-shared/chats';
import { useQueryClient } from '@tanstack/react-query';
import isEmpty from 'lodash/isEmpty';
import uniq from 'lodash/uniq';
import { useMemo, useRef } from 'react';
import { useGetChatMemoized, useGetChatMessageMemoized } from '@/api/buster_rest/chats';
import { reportsQueryKeys } from '@/api/query_keys/reports';
2025-08-15 01:59:13 +08:00
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
2025-09-19 07:42:11 +08:00
import { useUnmount } from '@/hooks/useUnmount';
import type { BusterChatMessage } from '../../asset_interfaces/chat';
2025-08-30 00:04:23 +08:00
import { useChatUpdate } from '../../buster_rest/chats/useChatUpdate';
import { chatQueryKeys } from '../../query_keys/chat';
import { dashboardQueryKeys } from '../../query_keys/dashboard';
import { metricsQueryKeys } from '../../query_keys/metric';
2025-08-30 00:04:23 +08:00
import { DEFAULT_UPDATE_OPERATIONS } from '../config';
import { useShape, useShapeStream } from '../instances';
import { updateMessageShapeToIChatMessage } from './helpers';
import { messageShape, messagesShape } from './shapes';
export const useGetMessage = ({ chatId, messageId }: { chatId: string; messageId: string }) => {
const shape = useMemo(() => messageShape({ chatId, messageId }), [chatId, messageId]);
return useShape(shape);
};
export const useGetMessages = ({ chatId }: { chatId: string }) => {
const shape = useMemo(() => messagesShape({ chatId }), [chatId]);
return useShape(shape);
};
const insertOperations: Array<'insert' | 'update' | 'delete'> = ['insert'];
export const useTrackAndUpdateMessageChanges = (
{
chatId,
messageId,
2025-09-26 06:06:28 +08:00
isStreamingMessage,
}: {
chatId: string | undefined;
messageId: string;
isStreamingMessage: boolean;
},
callback?: (message: ReturnType<typeof updateMessageShapeToIChatMessage>) => void
) => {
const { onUpdateChatMessage, onUpdateChat } = useChatUpdate();
const checkIfWeHaveAFollowupDashboard = useCheckIfWeHaveAFollowupDashboard(messageId);
const getChatMemoized = useGetChatMemoized();
const queryClient = useQueryClient();
2025-09-26 06:06:28 +08:00
const subscribe = !!chatId && !!messageId && messageId !== 'undefined' && isStreamingMessage;
2025-09-19 06:57:00 +08:00
const shape = useMemo(() => {
return messageShape({ chatId: chatId || '', messageId });
}, [chatId, messageId]);
return useShapeStream(
shape,
2025-08-30 00:04:23 +08:00
DEFAULT_UPDATE_OPERATIONS,
2025-08-15 01:59:13 +08:00
(message) => {
if (message?.value && chatId) {
const iChatMessage = updateMessageShapeToIChatMessage(message.value);
const chat = getChatMemoized(chatId);
if (chat) {
//ADD NEW MESSAGE ID TO CHAT
const currentMessageIds = chat.message_ids;
const allMessageIds = uniq([...currentMessageIds, messageId]);
if (currentMessageIds.length !== allMessageIds.length) {
onUpdateChat({
...chat,
id: chatId,
message_ids: allMessageIds,
});
}
if (!isEmpty(iChatMessage.response_message_ids)) {
checkIfWeHaveAFollowupDashboard(iChatMessage);
}
if (iChatMessage.is_completed) {
queryClient.invalidateQueries({
queryKey: chatQueryKeys.chatsGetList().queryKey,
});
const hasFiles = iChatMessage.reasoning_message_ids?.some((id) => {
const reasoningMessage = iChatMessage.response_messages?.[id];
return (
reasoningMessage &&
2025-09-18 04:37:14 +08:00
(reasoningMessage as ChatMessageResponseMessage_File)?.file_type ===
'dashboard_file'
);
});
if (hasFiles) {
queryClient
.invalidateQueries({
queryKey: metricsQueryKeys.metricsGetList().queryKey,
})
.then(() => {
queryClient.invalidateQueries({
queryKey: reportsQueryKeys.reportsGetList().queryKey,
});
});
}
}
}
callback?.(iChatMessage);
onUpdateChatMessage(iChatMessage);
}
2025-08-15 01:59:13 +08:00
},
subscribe
);
};
const useCheckIfWeHaveAFollowupDashboard = (messageId: string) => {
const queryClient = useQueryClient();
const hasSeenFileByMessageId = useRef<Record<string, boolean>>({});
const method = (message: Partial<BusterChatMessage>) => {
if (!hasSeenFileByMessageId.current[messageId]) {
const allFiles = Object.values(message.response_messages || {}).filter(
2025-09-18 04:37:14 +08:00
(x) => (x as ChatMessageResponseMessage_File).file_type === 'dashboard_file'
) as ChatMessageResponseMessage_File[];
if (allFiles.length > 0) {
hasSeenFileByMessageId.current[messageId] = true;
for (const file of allFiles) {
const fileType = (file as ChatMessageResponseMessage_File).file_type;
2025-09-18 04:37:14 +08:00
if (fileType === 'dashboard_file') {
2025-08-31 09:54:29 +08:00
const queryKey = dashboardQueryKeys
.dashboardGetDashboard(file.id, file.version_number)
.queryKey.slice(0, 3);
queryClient.invalidateQueries({
2025-08-31 09:54:29 +08:00
exact: false,
queryKey,
});
2025-09-18 04:37:14 +08:00
} else if (fileType === 'metric_file') {
2025-08-31 09:54:29 +08:00
const queryKey = metricsQueryKeys
.metricsGetMetric(file.id, file.version_number)
.queryKey.slice(0, 3);
queryClient.invalidateQueries({
2025-08-31 09:54:29 +08:00
exact: false,
queryKey,
});
2025-09-18 04:37:14 +08:00
} else if (fileType === 'report_file') {
2025-08-31 09:54:29 +08:00
const queryKey = reportsQueryKeys
.reportsGetReport(file.id, file.version_number)
.queryKey.slice(0, 3);
queryClient.invalidateQueries({
2025-08-31 09:54:29 +08:00
exact: false,
queryKey,
});
} else {
const _exhaustiveCheck: 'reasoning' = fileType;
}
}
}
}
};
return useMemoizedFn(method);
};
export const useTrackAndUpdateNewMessages = ({ chatId }: { chatId: string | undefined }) => {
const { onUpdateChat } = useChatUpdate();
const getChatMemoized = useGetChatMemoized();
const getChatMessageMemoized = useGetChatMessageMemoized();
const queryClient = useQueryClient();
const subscribe = !!chatId;
2025-09-19 06:57:00 +08:00
const shape = useMemo(() => {
return messagesShape({ chatId: chatId || '', columns: ['id'] });
}, [chatId]);
return useShapeStream(
shape,
insertOperations,
2025-08-30 00:04:23 +08:00
(message) => {
2025-08-15 01:59:13 +08:00
if (message?.value && chatId) {
const messageId = message.value.id;
const chat = getChatMemoized(chatId);
if (chat && messageId) {
const currentMessageIds = chat.message_ids;
const allMessageIds = uniq([...currentMessageIds, messageId]);
if (currentMessageIds.length !== allMessageIds.length) {
onUpdateChat({
...chat,
id: chatId,
message_ids: allMessageIds,
});
const messageIsStored = getChatMessageMemoized(messageId);
if (!messageIsStored) {
queryClient.invalidateQueries({
queryKey: chatQueryKeys.chatsGetChat(chatId).queryKey,
});
}
}
}
}
2025-08-30 00:04:23 +08:00
},
subscribe
);
};