2025-03-08 07:02:56 +08:00
|
|
|
'use client';
|
|
|
|
|
2025-02-09 13:41:08 +08:00
|
|
|
import React from 'react';
|
2025-03-08 07:02:56 +08:00
|
|
|
import { createContext, useContextSelector } from 'use-context-selector';
|
|
|
|
import { useMemoizedFn } from '@/hooks';
|
2025-02-11 11:15:32 +08:00
|
|
|
import type { BusterSearchResult, FileType } from '@/api/asset_interfaces';
|
2025-02-11 07:43:38 +08:00
|
|
|
import { useBusterWebSocket } from '@/context/BusterWebSocket';
|
2025-02-18 07:25:31 +08:00
|
|
|
import { useChatStreamMessage } from './useChatStreamMessage';
|
2025-03-30 11:13:40 +08:00
|
|
|
import { useGetChatMemoized, useGetChatMessageMemoized } from '@/api/buster_rest/chats';
|
|
|
|
import { useChatUpdate } from './useChatUpdate';
|
|
|
|
import { create } from 'mutative';
|
2025-02-01 06:21:50 +08:00
|
|
|
|
|
|
|
export const useBusterNewChat = () => {
|
2025-02-11 07:43:38 +08:00
|
|
|
const busterSocket = useBusterWebSocket();
|
2025-03-30 11:13:40 +08:00
|
|
|
const getChatMessageMemoized = useGetChatMessageMemoized();
|
|
|
|
const getChatMemoized = useGetChatMemoized();
|
|
|
|
const { onUpdateChat, onUpdateChatMessage } = useChatUpdate();
|
2025-02-11 07:43:38 +08:00
|
|
|
|
2025-03-30 11:13:40 +08:00
|
|
|
const { completeChatCallback, stopChatCallback, initializeNewChatCallback } =
|
|
|
|
useChatStreamMessage();
|
2025-02-11 07:43:38 +08:00
|
|
|
|
2025-02-05 13:10:39 +08:00
|
|
|
const onSelectSearchAsset = useMemoizedFn(async (asset: BusterSearchResult) => {
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
|
|
});
|
2025-02-01 06:21:50 +08:00
|
|
|
|
2025-02-12 03:32:12 +08:00
|
|
|
const onStartNewChat = useMemoizedFn(
|
|
|
|
async ({
|
|
|
|
prompt,
|
|
|
|
datasetId,
|
|
|
|
metricId,
|
|
|
|
dashboardId
|
|
|
|
}: {
|
|
|
|
prompt: string;
|
|
|
|
datasetId?: string;
|
|
|
|
metricId?: string;
|
|
|
|
dashboardId?: string;
|
|
|
|
}) => {
|
2025-03-05 00:32:47 +08:00
|
|
|
const res = await busterSocket.emitAndOnce({
|
2025-02-12 03:32:12 +08:00
|
|
|
emitEvent: {
|
|
|
|
route: '/chats/post',
|
|
|
|
payload: {
|
|
|
|
dataset_id: datasetId, //TODO: add selected dataset id
|
|
|
|
prompt,
|
|
|
|
metric_id: metricId,
|
|
|
|
dashboard_id: dashboardId
|
|
|
|
}
|
|
|
|
},
|
|
|
|
responseEvent: {
|
|
|
|
route: '/chats/post:initializeChat',
|
2025-02-12 07:46:22 +08:00
|
|
|
callback: initializeNewChatCallback
|
2025-02-12 02:22:27 +08:00
|
|
|
}
|
2025-02-12 03:32:12 +08:00
|
|
|
});
|
|
|
|
|
2025-02-18 07:25:31 +08:00
|
|
|
busterSocket.once({
|
|
|
|
route: '/chats/post:complete',
|
|
|
|
callback: completeChatCallback
|
|
|
|
});
|
2025-02-12 03:32:12 +08:00
|
|
|
}
|
|
|
|
);
|
2025-02-01 06:21:50 +08:00
|
|
|
|
2025-02-05 13:04:26 +08:00
|
|
|
const onStartChatFromFile = useMemoizedFn(
|
2025-02-12 07:46:22 +08:00
|
|
|
async ({
|
|
|
|
prompt,
|
|
|
|
fileId,
|
|
|
|
fileType
|
|
|
|
}: {
|
|
|
|
prompt: string;
|
|
|
|
fileId: string;
|
|
|
|
fileType: FileType;
|
|
|
|
}) => {
|
|
|
|
onStartNewChat({
|
|
|
|
prompt,
|
|
|
|
metricId: fileType === 'metric' ? fileId : undefined,
|
|
|
|
dashboardId: fileType === 'dashboard' ? fileId : undefined
|
|
|
|
});
|
2025-02-05 13:10:39 +08:00
|
|
|
}
|
2025-02-05 13:04:26 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
const onReplaceMessageInChat = useMemoizedFn(
|
2025-02-12 03:32:12 +08:00
|
|
|
async ({
|
|
|
|
prompt,
|
|
|
|
messageId,
|
|
|
|
chatId
|
|
|
|
}: {
|
|
|
|
prompt: string;
|
|
|
|
messageId: string;
|
|
|
|
chatId: string;
|
|
|
|
}) => {
|
2025-03-30 11:13:40 +08:00
|
|
|
const currentChat = getChatMemoized(chatId);
|
|
|
|
const currentMessage = getChatMessageMemoized(messageId);
|
|
|
|
const currentRequestMessage = currentMessage?.request_message!;
|
|
|
|
onUpdateChatMessage({
|
|
|
|
id: messageId,
|
|
|
|
request_message: create(currentRequestMessage, (draft) => {
|
|
|
|
draft.request = prompt;
|
|
|
|
}),
|
|
|
|
reasoning_message_ids: [],
|
|
|
|
response_message_ids: [],
|
|
|
|
isCompletedStream: false
|
2025-02-12 03:32:12 +08:00
|
|
|
});
|
2025-03-30 11:13:40 +08:00
|
|
|
|
|
|
|
const messageIndex = currentChat?.message_ids.findIndex(
|
|
|
|
(messageId) => messageId === messageId
|
|
|
|
);
|
|
|
|
|
|
|
|
if (messageIndex && messageIndex !== -1) {
|
|
|
|
const updatedMessageIds = currentChat?.message_ids.slice(0, messageIndex + 1);
|
|
|
|
onUpdateChat({
|
|
|
|
id: chatId,
|
|
|
|
message_ids: updatedMessageIds
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2025-02-12 03:32:12 +08:00
|
|
|
await busterSocket.emitAndOnce({
|
|
|
|
emitEvent: {
|
|
|
|
route: '/chats/post',
|
|
|
|
payload: {
|
|
|
|
prompt,
|
|
|
|
message_id: messageId,
|
|
|
|
chat_id: chatId
|
|
|
|
}
|
|
|
|
},
|
|
|
|
responseEvent: {
|
|
|
|
route: '/chats/post:complete',
|
|
|
|
callback: completeChatCallback
|
|
|
|
}
|
|
|
|
});
|
2025-02-05 13:10:39 +08:00
|
|
|
}
|
2025-02-05 13:04:26 +08:00
|
|
|
);
|
|
|
|
|
2025-02-11 07:43:38 +08:00
|
|
|
const onFollowUpChat = useMemoizedFn(
|
|
|
|
async ({ prompt, chatId }: { prompt: string; chatId: string }) => {
|
2025-03-12 02:11:18 +08:00
|
|
|
busterSocket.once({
|
|
|
|
route: '/chats/post:initializeChat',
|
|
|
|
callback: initializeNewChatCallback
|
|
|
|
});
|
2025-02-12 03:32:12 +08:00
|
|
|
await busterSocket.emitAndOnce({
|
2025-02-11 07:43:38 +08:00
|
|
|
emitEvent: {
|
|
|
|
route: '/chats/post',
|
|
|
|
payload: {
|
|
|
|
prompt,
|
|
|
|
chat_id: chatId
|
|
|
|
}
|
|
|
|
},
|
|
|
|
responseEvent: {
|
|
|
|
route: '/chats/post:complete',
|
|
|
|
callback: completeChatCallback
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
const onStopChat = useMemoizedFn(
|
|
|
|
({ chatId, messageId }: { chatId: string; messageId: string }) => {
|
|
|
|
busterSocket.emit({
|
|
|
|
route: '/chats/stop',
|
|
|
|
payload: {
|
|
|
|
id: chatId,
|
|
|
|
message_id: messageId
|
|
|
|
}
|
|
|
|
});
|
|
|
|
stopChatCallback(chatId);
|
|
|
|
}
|
|
|
|
);
|
2025-02-01 06:21:50 +08:00
|
|
|
|
|
|
|
return {
|
|
|
|
onStartNewChat,
|
|
|
|
onSelectSearchAsset,
|
2025-02-05 13:04:26 +08:00
|
|
|
onFollowUpChat,
|
|
|
|
onStartChatFromFile,
|
2025-02-06 00:55:09 +08:00
|
|
|
onReplaceMessageInChat,
|
|
|
|
onStopChat
|
2025-02-01 06:21:50 +08:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
export const BusterNewChatContext = createContext<ReturnType<typeof useBusterNewChat>>(
|
|
|
|
{} as ReturnType<typeof useBusterNewChat>
|
|
|
|
);
|
|
|
|
|
|
|
|
export const BusterNewChatProvider: React.FC<{
|
|
|
|
children: React.ReactNode;
|
|
|
|
}> = ({ children }) => {
|
|
|
|
const value = useBusterNewChat();
|
|
|
|
return <BusterNewChatContext.Provider value={value}>{children}</BusterNewChatContext.Provider>;
|
|
|
|
};
|
|
|
|
|
|
|
|
export const useBusterNewChatContextSelector = <T,>(
|
2025-03-08 07:02:56 +08:00
|
|
|
selector: (state: ReturnType<typeof useBusterNewChat>) => T
|
2025-02-01 06:21:50 +08:00
|
|
|
) => useContextSelector(BusterNewChatContext, selector);
|