mirror of https://github.com/buster-so/buster.git
added functions for streaming content
This commit is contained in:
parent
25e24b1341
commit
7a349da981
|
@ -80,6 +80,7 @@ export type BusterChatMessageReasoning_file = {
|
||||||
version_number: number;
|
version_number: number;
|
||||||
version_id: string;
|
version_id: string;
|
||||||
status: 'loading' | 'completed' | 'failed';
|
status: 'loading' | 'completed' | 'failed';
|
||||||
|
//when we are streaming, the whole file will always be streamed back, not chunks
|
||||||
file?: {
|
file?: {
|
||||||
text: string;
|
text: string;
|
||||||
line_number: number;
|
line_number: number;
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import type { BusterChatMessage, BusterChatMessageResponse } from './chatMessageInterfaces';
|
import type {
|
||||||
|
BusterChatMessage,
|
||||||
|
BusterChatMessageReasoning,
|
||||||
|
BusterChatMessageResponse
|
||||||
|
} from './chatMessageInterfaces';
|
||||||
|
|
||||||
enum BusterChatStepProgress {
|
enum BusterChatStepProgress {
|
||||||
IN_PROGRESS = 'in_progress',
|
IN_PROGRESS = 'in_progress',
|
||||||
|
@ -20,6 +24,10 @@ export type ChatPost_generatingMessage = {
|
||||||
resposnse_message: BusterChatMessageResponse;
|
resposnse_message: BusterChatMessageResponse;
|
||||||
} & BusterChatStepBase;
|
} & BusterChatStepBase;
|
||||||
|
|
||||||
|
export type ChatPost_generatingReasoning = {
|
||||||
|
reasoning: BusterChatMessageReasoning;
|
||||||
|
} & BusterChatStepBase;
|
||||||
|
|
||||||
export type ChatPost_complete = {
|
export type ChatPost_complete = {
|
||||||
message: BusterChatMessage;
|
message: BusterChatMessage;
|
||||||
} & BusterChatStepBase;
|
} & BusterChatStepBase;
|
||||||
|
|
|
@ -9,7 +9,7 @@ export type ChatCreateNewChat = BusterSocketRequestBase<
|
||||||
'/chats/post',
|
'/chats/post',
|
||||||
{
|
{
|
||||||
/** The ID of the dataset to associate with the chat. Null if no dataset is associated */
|
/** The ID of the dataset to associate with the chat. Null if no dataset is associated */
|
||||||
dataset_id: string | null;
|
dataset_id?: string | null;
|
||||||
/** The initial message or prompt to start the chat conversation */
|
/** The initial message or prompt to start the chat conversation */
|
||||||
prompt: string;
|
prompt: string;
|
||||||
/** Optional ID of an existing chat for follow-up messages. Null for new chats */
|
/** Optional ID of an existing chat for follow-up messages. Null for new chats */
|
||||||
|
@ -154,4 +154,5 @@ export type ChatEmits =
|
||||||
| ChatDeleteChat
|
| ChatDeleteChat
|
||||||
| ChatUpdateChat
|
| ChatUpdateChat
|
||||||
| ChatsSearch
|
| ChatsSearch
|
||||||
| ChatsDuplicateChat;
|
| ChatsDuplicateChat
|
||||||
|
| ChatStopChat;
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import type { RustApiError } from '../../buster_rest/errors';
|
import type { RustApiError } from '../../buster_rest/errors';
|
||||||
import type { BusterChat, BusterChatListItem } from '../../asset_interfaces/chat';
|
import type { BusterChat, BusterChatListItem } from '../../asset_interfaces/chat';
|
||||||
import { ChatEvent_GeneratingMessage, ChatEvent_GeneratingTitle } from './eventInterfaces';
|
import {
|
||||||
|
ChatEvent_GeneratingReasoningMessage,
|
||||||
|
ChatEvent_GeneratingResponseMessage,
|
||||||
|
ChatEvent_GeneratingTitle
|
||||||
|
} from './eventInterfaces';
|
||||||
|
|
||||||
export enum ChatsResponses {
|
export enum ChatsResponses {
|
||||||
'/chats/list:getChatsList' = '/chats/list:getChatsList',
|
'/chats/list:getChatsList' = '/chats/list:getChatsList',
|
||||||
|
@ -8,7 +12,10 @@ export enum ChatsResponses {
|
||||||
'/chats/get:getChat' = '/chats/get:getChat',
|
'/chats/get:getChat' = '/chats/get:getChat',
|
||||||
'/chats/get:getChatAsset' = '/chats/get:getChatAsset',
|
'/chats/get:getChatAsset' = '/chats/get:getChatAsset',
|
||||||
'/chats/post:initializeChat' = '/chats/post:initializeChat',
|
'/chats/post:initializeChat' = '/chats/post:initializeChat',
|
||||||
'/chats/post:generatingTitle' = '/chats/post:generatingTitle'
|
'/chats/post:generatingTitle' = '/chats/post:generatingTitle',
|
||||||
|
'/chats/post:generatingResponseMessage' = '/chats/post:generatingResponseMessage',
|
||||||
|
'/chats/post:generatingReasoningMessage' = '/chats/post:generatingReasoningMessage',
|
||||||
|
'/chats/post:complete' = '/chats/post:complete'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ChatList_getChatsList = {
|
export type ChatList_getChatsList = {
|
||||||
|
@ -56,9 +63,21 @@ export type ChatPost_generatingTitle = {
|
||||||
onError?: (d: unknown | RustApiError) => void;
|
onError?: (d: unknown | RustApiError) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ChatPost_generatingMessage = {
|
export type ChatPost_generatingResponseMessage = {
|
||||||
route: '/chats/post:generatingMessage';
|
route: '/chats/post:generatingResponseMessage';
|
||||||
callback: (d: ChatEvent_GeneratingMessage) => void;
|
callback: (d: ChatEvent_GeneratingResponseMessage) => void;
|
||||||
|
onError?: (d: unknown | RustApiError) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ChatPost_generatingReasoningMessage = {
|
||||||
|
route: '/chats/post:generatingReasoningMessage';
|
||||||
|
callback: (d: ChatEvent_GeneratingReasoningMessage) => void;
|
||||||
|
onError?: (d: unknown | RustApiError) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ChatPost_complete = {
|
||||||
|
route: '/chats/post:complete';
|
||||||
|
callback: (d: BusterChat) => void;
|
||||||
onError?: (d: unknown | RustApiError) => void;
|
onError?: (d: unknown | RustApiError) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -71,4 +90,6 @@ export type ChatResponseTypes =
|
||||||
| Chat_getChatAsset
|
| Chat_getChatAsset
|
||||||
| ChatPost_initializeChat
|
| ChatPost_initializeChat
|
||||||
| ChatPost_generatingTitle
|
| ChatPost_generatingTitle
|
||||||
| ChatPost_generatingMessage;
|
| ChatPost_generatingResponseMessage
|
||||||
|
| ChatPost_generatingReasoningMessage
|
||||||
|
| ChatPost_complete;
|
||||||
|
|
|
@ -1,37 +1,34 @@
|
||||||
import type { BusterChatMessageResponse } from '@/api/asset_interfaces/chat';
|
import type {
|
||||||
|
BusterChatMessageReasoning,
|
||||||
|
BusterChatMessageResponse
|
||||||
|
} from '@/api/asset_interfaces/chat';
|
||||||
import type { EventBase } from '../base_interfaces';
|
import type { EventBase } from '../base_interfaces';
|
||||||
|
|
||||||
/**
|
|
||||||
* Chat event interface for title generation process.
|
|
||||||
*
|
|
||||||
* @remarks
|
|
||||||
* This interface extends EventBase to include properties specific to
|
|
||||||
* the title generation process in chat events.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export type ChatEvent_GeneratingTitle = {
|
export type ChatEvent_GeneratingTitle = {
|
||||||
/** The complete generated title when available */
|
/** The complete generated title when available */
|
||||||
title?: string;
|
title?: string;
|
||||||
/** A partial chunk of the title during the generation process */
|
/** A partial chunk of the title during the generation process */
|
||||||
title_chunk?: string;
|
title_chunk?: string;
|
||||||
|
/** The ID of the chat that the title belongs to */
|
||||||
|
chat_id: string;
|
||||||
} & EventBase;
|
} & EventBase;
|
||||||
|
|
||||||
/**
|
export type ChatEvent_GeneratingResponseMessage = {
|
||||||
* Chat event interface for message generation process.
|
|
||||||
*
|
|
||||||
* @remarks
|
|
||||||
* This interface extends EventBase and handles the message generation process.
|
|
||||||
* When new messages are received, they are appended to the response_messages array.
|
|
||||||
* If a message with a matching ID already exists, it will be updated instead of
|
|
||||||
* creating a duplicate entry.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export type ChatEvent_GeneratingMessage = {
|
|
||||||
// We will append each incoming message to the response_messages array
|
// We will append each incoming message to the response_messages array
|
||||||
// If the message id is already found in the array, we will update the message with the new data
|
// If the message id is already found in the array, we will update the message with the new data
|
||||||
// This will happen when we need to "hide" a message
|
// This will happen when we need to "hide" a message
|
||||||
/** The chat message response containing the generated content */
|
/** The chat message response containing the generated content */
|
||||||
message: BusterChatMessageResponse;
|
response_message: BusterChatMessageResponse;
|
||||||
|
/** The ID of the chat that the response message belongs to */
|
||||||
|
chat_id: string;
|
||||||
|
/** The ID of the message that the response message belongs to */
|
||||||
|
message_id: string;
|
||||||
|
} & EventBase;
|
||||||
|
|
||||||
|
export type ChatEvent_GeneratingReasoningMessage = {
|
||||||
|
reasoning: BusterChatMessageReasoning;
|
||||||
|
/** The ID of the chat that the reasoning message belongs to */
|
||||||
|
chat_id: string;
|
||||||
|
/** The ID of the message that the reasoning message belongs to */
|
||||||
|
message_id: string;
|
||||||
} & EventBase;
|
} & EventBase;
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
export * from './chatRequests';
|
export * from './chatRequests';
|
||||||
export * from './chatResponses';
|
export * from './chatResponses';
|
||||||
|
export * from './eventInterfaces';
|
||||||
|
|
|
@ -18,8 +18,6 @@ export const ReasoningFileButtons = React.memo(
|
||||||
chatId: string;
|
chatId: string;
|
||||||
isCompletedStream: boolean;
|
isCompletedStream: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
if (!isCompletedStream) return null;
|
|
||||||
|
|
||||||
const onSetSelectedFile = useChatLayoutContextSelector((state) => state.onSetSelectedFile);
|
const onSetSelectedFile = useChatLayoutContextSelector((state) => state.onSetSelectedFile);
|
||||||
|
|
||||||
const link = createChatAssetRoute({
|
const link = createChatAssetRoute({
|
||||||
|
@ -37,6 +35,8 @@ export const ReasoningFileButtons = React.memo(
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!isCompletedStream) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<AppTooltip title="Open file">
|
<AppTooltip title="Open file">
|
||||||
|
|
|
@ -125,3 +125,5 @@ const MemoizedSyntaxHighlighter = React.memo(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
MemoizedSyntaxHighlighter.displayName = 'MemoizedSyntaxHighlighter';
|
||||||
|
|
|
@ -37,16 +37,16 @@ export const useChatInputFlow = ({
|
||||||
}, [hasChat, selectedFileType, selectedFileId]);
|
}, [hasChat, selectedFileType, selectedFileId]);
|
||||||
|
|
||||||
const onSubmitPreflight = useMemoizedFn(async () => {
|
const onSubmitPreflight = useMemoizedFn(async () => {
|
||||||
if (disableSendButton) return;
|
if (disableSendButton || !chatId || !currentMessageId) return;
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
onStopChat({ chatId: chatId! });
|
onStopChat({ chatId: chatId!, messageId: currentMessageId });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (flow) {
|
switch (flow) {
|
||||||
case 'followup-chat':
|
case 'followup-chat':
|
||||||
await onFollowUpChat({ prompt: inputValue, messageId: currentMessageId! });
|
await onFollowUpChat({ prompt: inputValue, chatId: chatId });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'followup-metric':
|
case 'followup-metric':
|
||||||
|
|
|
@ -16,7 +16,6 @@ export const useAutoSetLayout = ({
|
||||||
}, [defaultSelectedLayout]);
|
}, [defaultSelectedLayout]);
|
||||||
|
|
||||||
const collapseDirection: 'left' | 'right' = useMemo(() => {
|
const collapseDirection: 'left' | 'right' = useMemo(() => {
|
||||||
console.log(defaultSelectedLayout);
|
|
||||||
return defaultSelectedLayout === 'file' ? 'left' : 'right';
|
return defaultSelectedLayout === 'file' ? 'left' : 'right';
|
||||||
}, [defaultSelectedLayout]);
|
}, [defaultSelectedLayout]);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { MutableRefObject, useCallback } from 'react';
|
import { MutableRefObject, useCallback } from 'react';
|
||||||
import { IBusterChat, IBusterChatMessage } from '../interfaces';
|
import { IBusterChat, IBusterChatMessage } from '../interfaces';
|
||||||
|
import { useMemoizedFn } from 'ahooks';
|
||||||
|
|
||||||
export const useChatSelectors = ({
|
export const useChatSelectors = ({
|
||||||
isPending,
|
isPending,
|
||||||
|
@ -10,6 +11,10 @@ export const useChatSelectors = ({
|
||||||
chatsRef: MutableRefObject<Record<string, IBusterChat>>;
|
chatsRef: MutableRefObject<Record<string, IBusterChat>>;
|
||||||
chatsMessagesRef: MutableRefObject<Record<string, IBusterChatMessage>>;
|
chatsMessagesRef: MutableRefObject<Record<string, IBusterChatMessage>>;
|
||||||
}) => {
|
}) => {
|
||||||
|
const getChatMemoized = useMemoizedFn((chatId: string) => {
|
||||||
|
return chatsRef.current[chatId];
|
||||||
|
});
|
||||||
|
|
||||||
const getChatMessages = useCallback(
|
const getChatMessages = useCallback(
|
||||||
(chatId: string): IBusterChatMessage[] => {
|
(chatId: string): IBusterChatMessage[] => {
|
||||||
const chatMessageIds = chatsRef.current[chatId].messages || [];
|
const chatMessageIds = chatsRef.current[chatId].messages || [];
|
||||||
|
@ -25,5 +30,19 @@ export const useChatSelectors = ({
|
||||||
[chatsMessagesRef, isPending]
|
[chatsMessagesRef, isPending]
|
||||||
);
|
);
|
||||||
|
|
||||||
return { getChatMessages, getChatMessage };
|
const getChatMessagesMemoized = useMemoizedFn((chatId: string) => {
|
||||||
|
return getChatMessages(chatId);
|
||||||
|
});
|
||||||
|
|
||||||
|
const getChatMessageMemoized = useMemoizedFn((messageId: string) => {
|
||||||
|
return getChatMessage(messageId);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
getChatMemoized,
|
||||||
|
getChatMessages,
|
||||||
|
getChatMessage,
|
||||||
|
getChatMessagesMemoized,
|
||||||
|
getChatMessageMemoized
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,23 +1,10 @@
|
||||||
import { MutableRefObject } from 'react';
|
import { MutableRefObject } from 'react';
|
||||||
import { useBusterWebSocket } from '../../BusterWebSocket';
|
import { useBusterWebSocket } from '../../BusterWebSocket';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
import {
|
import type { BusterChat } from '@/api/asset_interfaces';
|
||||||
BusterChat,
|
|
||||||
BusterChatMessage,
|
|
||||||
BusterChatMessageReasoning_thought,
|
|
||||||
BusterChatMessageReasoning_file
|
|
||||||
} from '@/api/asset_interfaces';
|
|
||||||
import { IBusterChat, IBusterChatMessage } from '../interfaces';
|
import { IBusterChat, IBusterChatMessage } from '../interfaces';
|
||||||
import { chatMessageUpgrader, chatUpgrader } from './helpers';
|
import { chatMessageUpgrader, chatUpgrader } from './helpers';
|
||||||
import {
|
import { MOCK_CHAT } from './MOCK_CHAT';
|
||||||
createMockResponseMessageFile,
|
|
||||||
createMockResponseMessageText,
|
|
||||||
createMockResponseMessageThought,
|
|
||||||
createMockReasoningMessageFile,
|
|
||||||
MOCK_CHAT
|
|
||||||
} from './MOCK_CHAT';
|
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
|
||||||
import { faker } from '@faker-js/faker';
|
|
||||||
|
|
||||||
export const useChatSubscriptions = ({
|
export const useChatSubscriptions = ({
|
||||||
chatsRef,
|
chatsRef,
|
||||||
|
@ -67,113 +54,6 @@ export const useChatSubscriptions = ({
|
||||||
// });
|
// });
|
||||||
});
|
});
|
||||||
|
|
||||||
useHotkeys('f', () => {
|
|
||||||
// Find the last chat message
|
|
||||||
const lastChatId = Object.keys(chatsRef.current)[Object.keys(chatsRef.current).length - 1];
|
|
||||||
const lastChat = chatsRef.current[lastChatId];
|
|
||||||
|
|
||||||
if (!lastChat?.messages?.length) return;
|
|
||||||
|
|
||||||
const lastMessageId = lastChat.messages[lastChat.messages.length - 1];
|
|
||||||
const lastMessage = chatsMessagesRef.current[lastMessageId];
|
|
||||||
|
|
||||||
if (!lastMessage?.reasoning?.length) return;
|
|
||||||
|
|
||||||
// Find the last reasoning file message
|
|
||||||
const lastReasoningFile = lastMessage.reasoning
|
|
||||||
.filter((r: { type: string }) => r.type === 'file')
|
|
||||||
.pop() as BusterChatMessageReasoning_file | undefined;
|
|
||||||
|
|
||||||
if (!lastReasoningFile) return;
|
|
||||||
|
|
||||||
// Create new file chunk
|
|
||||||
const newChunk = {
|
|
||||||
text: faker.lorem.sentence(),
|
|
||||||
line_number: (lastReasoningFile.file?.length || 0) + 1,
|
|
||||||
modified: true
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create new reasoning file with appended chunk
|
|
||||||
const updatedReasoningFile = {
|
|
||||||
...lastReasoningFile,
|
|
||||||
file: [...(lastReasoningFile.file || []), newChunk]
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create new message with updated reasoning array
|
|
||||||
const updatedMessage = {
|
|
||||||
...lastMessage,
|
|
||||||
reasoning: lastMessage.reasoning.map((r) => {
|
|
||||||
if (r.type === 'file' && r.id === lastReasoningFile.id) {
|
|
||||||
return updatedReasoningFile;
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update the refs with new object references
|
|
||||||
chatsMessagesRef.current = {
|
|
||||||
...chatsMessagesRef.current,
|
|
||||||
[lastMessageId]: updatedMessage
|
|
||||||
};
|
|
||||||
|
|
||||||
chatsRef.current = {
|
|
||||||
...chatsRef.current,
|
|
||||||
[lastChatId]: {
|
|
||||||
...lastChat,
|
|
||||||
messages: [...lastChat.messages]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
startTransition(() => {
|
|
||||||
// Force a re-render
|
|
||||||
chatsRef.current = { ...chatsRef.current };
|
|
||||||
chatsMessagesRef.current = { ...chatsMessagesRef.current };
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
useHotkeys('y', () => {
|
|
||||||
// Find the last chat message
|
|
||||||
const lastChatId = Object.keys(chatsRef.current)[Object.keys(chatsRef.current).length - 1];
|
|
||||||
const lastChat = chatsRef.current[lastChatId];
|
|
||||||
|
|
||||||
if (!lastChat?.messages?.length) return;
|
|
||||||
|
|
||||||
const lastMessageId = lastChat.messages[lastChat.messages.length - 1];
|
|
||||||
const lastMessage = chatsMessagesRef.current[lastMessageId];
|
|
||||||
|
|
||||||
if (!lastMessage) return;
|
|
||||||
lastMessage.isCompletedStream = false;
|
|
||||||
|
|
||||||
// Create a new reasoning file message
|
|
||||||
const newReasoningFile = createMockReasoningMessageFile();
|
|
||||||
|
|
||||||
// Add the new reasoning file to the reasoning array
|
|
||||||
const updatedMessage = {
|
|
||||||
...lastMessage,
|
|
||||||
reasoning: [...(lastMessage.reasoning || []), newReasoningFile]
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update the refs with new object references
|
|
||||||
chatsMessagesRef.current = {
|
|
||||||
...chatsMessagesRef.current,
|
|
||||||
[lastMessageId]: updatedMessage
|
|
||||||
};
|
|
||||||
|
|
||||||
chatsRef.current = {
|
|
||||||
...chatsRef.current,
|
|
||||||
[lastChatId]: {
|
|
||||||
...lastChat,
|
|
||||||
messages: [...lastChat.messages]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
startTransition(() => {
|
|
||||||
// Force a re-render
|
|
||||||
chatsRef.current = { ...chatsRef.current };
|
|
||||||
chatsMessagesRef.current = { ...chatsMessagesRef.current };
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
unsubscribeFromChat,
|
unsubscribeFromChat,
|
||||||
subscribeToChat
|
subscribeToChat
|
||||||
|
|
|
@ -6,8 +6,19 @@ import {
|
||||||
} from '@fluentui/react-context-selector';
|
} from '@fluentui/react-context-selector';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
import type { BusterDatasetListItem, BusterSearchResult, FileType } from '@/api/asset_interfaces';
|
import type { BusterDatasetListItem, BusterSearchResult, FileType } from '@/api/asset_interfaces';
|
||||||
|
import { useBusterWebSocket } from '@/context/BusterWebSocket';
|
||||||
|
import { useChatUpdateMessage } from './useChatUpdateMessage';
|
||||||
|
|
||||||
export const useBusterNewChat = () => {
|
export const useBusterNewChat = () => {
|
||||||
|
const busterSocket = useBusterWebSocket();
|
||||||
|
|
||||||
|
const {
|
||||||
|
completeChatCallback,
|
||||||
|
startListeningForChatProgress,
|
||||||
|
stopListeningForChatProgress,
|
||||||
|
stopChatCallback
|
||||||
|
} = useChatUpdateMessage();
|
||||||
|
|
||||||
const onSelectSearchAsset = useMemoizedFn(async (asset: BusterSearchResult) => {
|
const onSelectSearchAsset = useMemoizedFn(async (asset: BusterSearchResult) => {
|
||||||
console.log('select search asset');
|
console.log('select search asset');
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
|
@ -25,13 +36,6 @@ export const useBusterNewChat = () => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const onFollowUpChat = useMemoizedFn(
|
|
||||||
async ({ prompt, messageId }: { prompt: string; messageId: string }) => {
|
|
||||||
console.log('follow up chat');
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const onReplaceMessageInChat = useMemoizedFn(
|
const onReplaceMessageInChat = useMemoizedFn(
|
||||||
async ({ prompt, messageId }: { prompt: string; messageId: string }) => {
|
async ({ prompt, messageId }: { prompt: string; messageId: string }) => {
|
||||||
console.log('replace message in chat');
|
console.log('replace message in chat');
|
||||||
|
@ -39,18 +43,45 @@ export const useBusterNewChat = () => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const onStopChat = useMemoizedFn(({ chatId }: { chatId: string }) => {
|
const onFollowUpChat = useMemoizedFn(
|
||||||
console.log('stop current chat');
|
async ({ prompt, chatId }: { prompt: string; chatId: string }) => {
|
||||||
});
|
startListeningForChatProgress();
|
||||||
|
const result = await busterSocket.emitAndOnce({
|
||||||
|
emitEvent: {
|
||||||
|
route: '/chats/post',
|
||||||
|
payload: {
|
||||||
|
dataset_id: null,
|
||||||
|
prompt,
|
||||||
|
chat_id: chatId
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responseEvent: {
|
||||||
|
route: '/chats/post:complete',
|
||||||
|
callback: completeChatCallback
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const onSetSelectedChatDataSource = useMemoizedFn((dataSource: BusterDatasetListItem | null) => {
|
stopListeningForChatProgress();
|
||||||
//
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
|
const onStopChat = useMemoizedFn(
|
||||||
|
({ chatId, messageId }: { chatId: string; messageId: string }) => {
|
||||||
|
busterSocket.emit({
|
||||||
|
route: '/chats/stop',
|
||||||
|
payload: {
|
||||||
|
id: chatId,
|
||||||
|
message_id: messageId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stopListeningForChatProgress();
|
||||||
|
stopChatCallback(chatId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onStartNewChat,
|
onStartNewChat,
|
||||||
onSelectSearchAsset,
|
onSelectSearchAsset,
|
||||||
onSetSelectedChatDataSource,
|
|
||||||
onFollowUpChat,
|
onFollowUpChat,
|
||||||
onStartChatFromFile,
|
onStartChatFromFile,
|
||||||
onReplaceMessageInChat,
|
onReplaceMessageInChat,
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
import { useMemoizedFn } from 'ahooks';
|
||||||
|
import { useBusterChatContextSelector } from '../ChatProvider';
|
||||||
|
import { useBusterWebSocket } from '@/context/BusterWebSocket';
|
||||||
|
import { BusterChat } from '@/api/asset_interfaces';
|
||||||
|
import {
|
||||||
|
ChatEvent_GeneratingReasoningMessage,
|
||||||
|
ChatEvent_GeneratingResponseMessage,
|
||||||
|
ChatEvent_GeneratingTitle
|
||||||
|
} from '@/api/buster_socket/chats';
|
||||||
|
|
||||||
|
export const useChatUpdateMessage = () => {
|
||||||
|
const busterSocket = useBusterWebSocket();
|
||||||
|
const onUpdateChat = useBusterChatContextSelector((x) => x.onUpdateChat);
|
||||||
|
const getChatMemoized = useBusterChatContextSelector((x) => x.getChatMemoized);
|
||||||
|
const onUpdateChatMessage = useBusterChatContextSelector((x) => x.onUpdateChatMessage);
|
||||||
|
const getChatMessageMemoized = useBusterChatContextSelector((x) => x.getChatMessageMemoized);
|
||||||
|
|
||||||
|
const _generatingTitleCallback = useMemoizedFn((d: ChatEvent_GeneratingTitle) => {
|
||||||
|
const { chat_id, title, title_chunk } = d;
|
||||||
|
const isCompleted = d.progress === 'completed';
|
||||||
|
const currentTitle = getChatMemoized(chat_id)?.title;
|
||||||
|
const newTitle = isCompleted ? title : currentTitle + title_chunk;
|
||||||
|
onUpdateChat({
|
||||||
|
...getChatMemoized(chat_id),
|
||||||
|
title: newTitle
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const _generatingResponseMessageCallback = useMemoizedFn(
|
||||||
|
(d: ChatEvent_GeneratingResponseMessage) => {
|
||||||
|
const { message_id, response_message } = d;
|
||||||
|
const currentResponseMessages = getChatMessageMemoized(message_id)?.response_messages ?? [];
|
||||||
|
const isNewMessage = !currentResponseMessages.some(({ id }) => id === message_id);
|
||||||
|
onUpdateChatMessage({
|
||||||
|
id: message_id,
|
||||||
|
response_messages: isNewMessage
|
||||||
|
? [...currentResponseMessages, response_message]
|
||||||
|
: currentResponseMessages.map((rm) => (rm.id === message_id ? response_message : rm))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const _generatingReasoningMessageCallback = useMemoizedFn(
|
||||||
|
(d: ChatEvent_GeneratingReasoningMessage) => {
|
||||||
|
const { message_id, reasoning } = d;
|
||||||
|
const currentReasoning = getChatMessageMemoized(message_id)?.reasoning;
|
||||||
|
const isNewMessage = !currentReasoning?.some(({ id }) => id === message_id);
|
||||||
|
const updatedReasoning = isNewMessage
|
||||||
|
? [...currentReasoning, reasoning]
|
||||||
|
: currentReasoning.map((rm) => (rm.id === message_id ? reasoning : rm));
|
||||||
|
|
||||||
|
onUpdateChatMessage({
|
||||||
|
id: message_id,
|
||||||
|
reasoning: updatedReasoning
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const completeChatCallback = useMemoizedFn((d: BusterChat) => {
|
||||||
|
onUpdateChatMessage({
|
||||||
|
...d,
|
||||||
|
isCompletedStream: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const stopChatCallback = useMemoizedFn((chatId: string) => {
|
||||||
|
onUpdateChatMessage({
|
||||||
|
id: chatId,
|
||||||
|
isCompletedStream: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const listenForGeneratingTitle = useMemoizedFn(() => {
|
||||||
|
busterSocket.on({
|
||||||
|
route: '/chats/post:generatingTitle',
|
||||||
|
callback: _generatingTitleCallback
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const stopListeningForGeneratingTitle = useMemoizedFn(() => {
|
||||||
|
busterSocket.off({
|
||||||
|
route: '/chats/post:generatingTitle',
|
||||||
|
callback: _generatingTitleCallback
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const listenForGeneratingResponseMessage = useMemoizedFn(() => {
|
||||||
|
busterSocket.on({
|
||||||
|
route: '/chats/post:generatingResponseMessage',
|
||||||
|
callback: _generatingResponseMessageCallback
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const stopListeningForGeneratingResponseMessage = useMemoizedFn(() => {
|
||||||
|
busterSocket.off({
|
||||||
|
route: '/chats/post:generatingResponseMessage',
|
||||||
|
callback: _generatingResponseMessageCallback
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const listenForGeneratingReasoningMessage = useMemoizedFn(() => {
|
||||||
|
busterSocket.on({
|
||||||
|
route: '/chats/post:generatingReasoningMessage',
|
||||||
|
callback: _generatingReasoningMessageCallback
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const stopListeningForGeneratingReasoningMessage = useMemoizedFn(() => {
|
||||||
|
busterSocket.off({
|
||||||
|
route: '/chats/post:generatingReasoningMessage',
|
||||||
|
callback: _generatingReasoningMessageCallback
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const startListeningForChatProgress = useMemoizedFn(() => {
|
||||||
|
listenForGeneratingTitle();
|
||||||
|
listenForGeneratingResponseMessage();
|
||||||
|
listenForGeneratingReasoningMessage();
|
||||||
|
});
|
||||||
|
|
||||||
|
const stopListeningForChatProgress = useMemoizedFn(() => {
|
||||||
|
stopListeningForGeneratingTitle();
|
||||||
|
stopListeningForGeneratingResponseMessage();
|
||||||
|
stopListeningForGeneratingReasoningMessage();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
completeChatCallback,
|
||||||
|
startListeningForChatProgress,
|
||||||
|
stopListeningForChatProgress,
|
||||||
|
stopChatCallback
|
||||||
|
};
|
||||||
|
};
|
Loading…
Reference in New Issue