buster/web/src/context/Chats/NewChatProvider.tsx

190 lines
5.1 KiB
TypeScript
Raw Normal View History

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);