Merge pull request #562 from buster-so/devin/BUS-1447-1752896644

feat(BUS-1447): Add hook to track new message insertions from external sources
This commit is contained in:
Nate Kelley 2025-07-21 15:45:18 -06:00 committed by GitHub
commit d141cc5987
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 83 additions and 11 deletions

View File

@ -4,7 +4,7 @@ import { useShape, useShapeStream } from '../instances';
import { useChatUpdate } from '@/context/Chats/useChatUpdate'; import { useChatUpdate } from '@/context/Chats/useChatUpdate';
import { updateMessageShapeToIChatMessage } from './helpers'; import { updateMessageShapeToIChatMessage } from './helpers';
import { useMemoizedFn } from '@/hooks'; import { useMemoizedFn } from '@/hooks';
import { prefetchGetChatsList, useGetChatMemoized } from '@/api/buster_rest/chats'; import { useGetChatMemoized, useGetChatMessageMemoized } from '@/api/buster_rest/chats';
import uniq from 'lodash/uniq'; import uniq from 'lodash/uniq';
import type { ChatMessageResponseMessage_File } from '@buster/server-shared/chats'; import type { ChatMessageResponseMessage_File } from '@buster/server-shared/chats';
import type { BusterChatMessage } from '../../asset_interfaces/chat'; import type { BusterChatMessage } from '../../asset_interfaces/chat';
@ -12,6 +12,7 @@ import { useQueryClient } from '@tanstack/react-query';
import { dashboardQueryKeys } from '../../query_keys/dashboard'; import { dashboardQueryKeys } from '../../query_keys/dashboard';
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
import { metricsQueryKeys } from '../../query_keys/metric'; import { metricsQueryKeys } from '../../query_keys/metric';
import { chatQueryKeys } from '../../query_keys/chat';
export const useGetMessage = ({ chatId, messageId }: { chatId: string; messageId: string }) => { export const useGetMessage = ({ chatId, messageId }: { chatId: string; messageId: string }) => {
const shape = useMemo(() => messageShape({ chatId, messageId }), [chatId, messageId]); const shape = useMemo(() => messageShape({ chatId, messageId }), [chatId, messageId]);
@ -24,6 +25,7 @@ export const useGetMessages = ({ chatId }: { chatId: string }) => {
}; };
const updateOperations: Array<'insert' | 'update' | 'delete'> = ['update']; const updateOperations: Array<'insert' | 'update' | 'delete'> = ['update'];
const insertOperations: Array<'insert' | 'update' | 'delete'> = ['insert'];
export const useTrackAndUpdateMessageChanges = ( export const useTrackAndUpdateMessageChanges = (
{ {
@ -40,6 +42,7 @@ export const useTrackAndUpdateMessageChanges = (
const { onUpdateChatMessage, onUpdateChat } = useChatUpdate(); const { onUpdateChatMessage, onUpdateChat } = useChatUpdate();
const checkIfWeHaveAFollowupDashboard = useCheckIfWeHaveAFollowupDashboard(messageId); const checkIfWeHaveAFollowupDashboard = useCheckIfWeHaveAFollowupDashboard(messageId);
const getChatMemoized = useGetChatMemoized(); const getChatMemoized = useGetChatMemoized();
const queryClient = useQueryClient();
const subscribe = !!chatId && !!messageId && messageId !== 'undefined'; const subscribe = !!chatId && !!messageId && messageId !== 'undefined';
@ -63,6 +66,7 @@ export const useTrackAndUpdateMessageChanges = (
if (currentMessageIds.length !== allMessageIds.length) { if (currentMessageIds.length !== allMessageIds.length) {
onUpdateChat({ onUpdateChat({
...chat, ...chat,
id: chatId,
message_ids: allMessageIds message_ids: allMessageIds
}); });
} }
@ -75,16 +79,20 @@ export const useTrackAndUpdateMessageChanges = (
(reasoningMessage as ChatMessageResponseMessage_File)?.file_type === 'dashboard' (reasoningMessage as ChatMessageResponseMessage_File)?.file_type === 'dashboard'
); );
}); });
if (hasFiles) {
prefetchGetChatsList();
}
if (!isEmpty(iChatMessage.response_message_ids)) { if (!isEmpty(iChatMessage.response_message_ids)) {
checkIfWeHaveAFollowupDashboard(iChatMessage); checkIfWeHaveAFollowupDashboard(iChatMessage);
} }
if (iChatMessage.is_completed) { if (iChatMessage.is_completed) {
prefetchGetChatsList(); queryClient.invalidateQueries({
queryKey: chatQueryKeys.chatsGetList().queryKey
});
if (hasFiles) {
queryClient.invalidateQueries({
queryKey: metricsQueryKeys.metricsGetList().queryKey
});
}
} }
} }
callback?.(iChatMessage); callback?.(iChatMessage);
@ -128,3 +136,46 @@ const useCheckIfWeHaveAFollowupDashboard = (messageId: string) => {
return useMemoizedFn(method); 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;
const shape = useMemo(() => messagesShape({ chatId: chatId || '', columns: ['id'] }), [chatId]);
return useShapeStream(
shape,
insertOperations,
useMemoizedFn((message) => {
if (message && 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
});
}
}
}
}
}),
subscribe
);
};

View File

@ -15,7 +15,7 @@ export type BusterChatMessageShape = {
is_completed: boolean; is_completed: boolean;
}; };
const columns: (keyof BusterChatMessageShape)[] = [ const MESSAGE_DEFAULT_COLUMNS: (keyof BusterChatMessageShape)[] = [
'id', 'id',
'response_messages', 'response_messages',
'reasoning', 'reasoning',
@ -36,18 +36,24 @@ export const messageShape = ({
params: { params: {
table: 'messages', table: 'messages',
where: `chat_id='${chatId}' AND id='${messageId}'`, where: `chat_id='${chatId}' AND id='${messageId}'`,
columns, columns: MESSAGE_DEFAULT_COLUMNS,
replica: 'default' replica: 'default'
} }
}; };
}; };
export const messagesShape = ({ export const messagesShape = ({
chatId chatId,
columns = MESSAGE_DEFAULT_COLUMNS
}: { }: {
chatId: string; chatId: string;
columns?: (keyof BusterChatMessageShape)[];
}): ElectricShapeOptions<BusterChatMessageShape> => { }): ElectricShapeOptions<BusterChatMessageShape> => {
return { return {
params: { table: 'messages', where: `chat_id='${chatId}'`, columns } params: {
table: 'messages',
where: `chat_id='${chatId}'`,
columns: columns || MESSAGE_DEFAULT_COLUMNS
}
}; };
}; };

View File

@ -152,7 +152,7 @@ export const useStartChatFromAsset = () => {
}); });
}; };
export const prefetchGetChat = async ( export const prefetchGetChatServer = async (
params: Parameters<typeof getChat>[0], params: Parameters<typeof getChat>[0],
queryClientProp?: QueryClient queryClientProp?: QueryClient
) => { ) => {
@ -170,6 +170,20 @@ export const prefetchGetChat = async (
return queryClient; return queryClient;
}; };
export const prefetchGetChat = async (
params: Parameters<typeof getChat>[0],
queryClientProp?: QueryClient
) => {
const queryClient = queryClientProp || new QueryClient();
await queryClient.prefetchQuery({
...chatQueryKeys.chatsGetChat(params.id),
queryFn: () => getChat(params)
});
return queryClient;
};
export const useUpdateChat = (params?: { updateToServer?: boolean }) => { export const useUpdateChat = (params?: { updateToServer?: boolean }) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { updateToServer = true } = params || {}; const { updateToServer = true } = params || {};

View File

@ -5,7 +5,7 @@ import { updateChatToIChat } from '@/lib/chat';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { prefetchGetMetricDataClient } from '@/api/buster_rest/metrics'; import { prefetchGetMetricDataClient } from '@/api/buster_rest/metrics';
import { queryKeys } from '@/api/query_keys'; import { queryKeys } from '@/api/query_keys';
import { useTrackAndUpdateMessageChanges } from '@/api/buster-electric/messages'; import { useTrackAndUpdateMessageChanges, useTrackAndUpdateNewMessages } from '@/api/buster-electric/messages';
import { useTrackAndUpdateChatChanges } from '@/api/buster-electric/chats'; import { useTrackAndUpdateChatChanges } from '@/api/buster-electric/chats';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useGetChatMessageMemoized } from '@/api/buster_rest/chats'; import { useGetChatMessageMemoized } from '@/api/buster_rest/chats';
@ -90,6 +90,7 @@ export const useChatStreaming = ({
//HOOKS FOR TRACKING CHAT AND MESSAGE CHANGES //HOOKS FOR TRACKING CHAT AND MESSAGE CHANGES
useTrackAndUpdateChatChanges({ chatId, isStreamingMessage }); useTrackAndUpdateChatChanges({ chatId, isStreamingMessage });
useTrackAndUpdateNewMessages({ chatId });
useTrackAndUpdateMessageChanges({ chatId, messageId, isStreamingMessage }, (c) => { useTrackAndUpdateMessageChanges({ chatId, messageId, isStreamingMessage }, (c) => {
const { const {
reasoning_messages, reasoning_messages,