chat update

This commit is contained in:
Nate Kelley 2025-02-10 20:15:32 -07:00
parent 7a349da981
commit 644d7f89d8
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
20 changed files with 77 additions and 62 deletions

View File

@ -5,9 +5,5 @@ export default function Page({
}: { }: {
params: { chatId: string; messageId: string }; params: { chatId: string; messageId: string };
}) { }) {
return ( return <ReasoningController chatId={chatId} messageId={messageId} />;
<>
<ReasoningController chatId={chatId} messageId={messageId} />
</>
);
} }

View File

@ -1,5 +1,5 @@
import { createStyles } from 'antd-style'; import { createStyles } from 'antd-style';
import { Text } from '@/components'; import { Text } from '@/components/text';
import React from 'react'; import React from 'react';
export const VersionPill: React.FC<{ version_number: number }> = React.memo( export const VersionPill: React.FC<{ version_number: number }> = React.memo(

View File

@ -1,5 +1,5 @@
'use client'; 'use client';
import React, { useMemo } from 'react'; import React from 'react';
import { useChatIndividualContextSelector } from '../../_layouts/ChatLayout/ChatContext'; import { useChatIndividualContextSelector } from '../../_layouts/ChatLayout/ChatContext';
import { ReasoningMessageContainer } from './ReasoningMessageContainer'; import { ReasoningMessageContainer } from './ReasoningMessageContainer';
import { useBusterChatContextSelector } from '@/context/Chats'; import { useBusterChatContextSelector } from '@/context/Chats';

View File

@ -1,6 +1,7 @@
import type { FileType } from '@/api/asset_interfaces'; import type { FileType } from '@/api/asset_interfaces';
import { createChatAssetRoute } from '@appLayouts/ChatLayout/ChatLayoutContext/helpers'; import { createChatAssetRoute } from '@appLayouts/ChatLayout/ChatLayoutContext/helpers';
import { AppMaterialIcons, AppTooltip } from '@/components'; import { AppTooltip } from '@/components/tooltip';
import { AppMaterialIcons } from '@/components/icons';
import { Button } from 'antd'; import { Button } from 'antd';
import React from 'react'; import React from 'react';
import { useChatLayoutContextSelector } from '@/app/app/_layouts/ChatLayout'; import { useChatLayoutContextSelector } from '@/app/app/_layouts/ChatLayout';

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { Text } from '@/components'; import { Text } from '@/components/text';
import { VersionPill } from '@/app/app/_components/Text/VersionPill'; import { VersionPill } from '@appComponents/Text/VersionPill';
export const ReasoningFileTitle = React.memo( export const ReasoningFileTitle = React.memo(
({ file_name, version_number }: { file_name: string; version_number: number }) => { ({ file_name, version_number }: { file_name: string; version_number: number }) => {

View File

@ -13,14 +13,11 @@ const autoSize = { minRows: 3, maxRows: 16 };
export const ChatInput: React.FC<{}> = React.memo(({}) => { export const ChatInput: React.FC<{}> = React.memo(({}) => {
const { styles, cx } = useStyles(); const { styles, cx } = useStyles();
const isNewChat = useChatIndividualContextSelector((x) => x.isNewChat);
const isFollowUpChat = useChatIndividualContextSelector((x) => x.isFollowUpChat);
const inputRef = useRef<TextAreaRef>(null); const inputRef = useRef<TextAreaRef>(null);
const loading = useChatIndividualContextSelector((x) => x.isLoading);
const [inputValue, setInputValue] = useState(''); const [inputValue, setInputValue] = useState('');
const [isFocused, setIsFocused] = React.useState(false); const [isFocused, setIsFocused] = useState(false);
const loading = isNewChat || isFollowUpChat;
const disableSendButton = useMemo(() => { const disableSendButton = useMemo(() => {
return !inputHasText(inputValue); return !inputHasText(inputValue);

View File

@ -7,6 +7,7 @@ import {
import type { SelectedFile } from '../interfaces'; import type { SelectedFile } from '../interfaces';
import { useSubscribeIndividualChat } from './useSubscribeIndividualChat'; import { useSubscribeIndividualChat } from './useSubscribeIndividualChat';
import { useAutoChangeLayout } from './useAutoChangeLayout'; import { useAutoChangeLayout } from './useAutoChangeLayout';
import { useBusterChatContextSelector } from '@/context/Chats';
export const useChatIndividualContext = ({ export const useChatIndividualContext = ({
chatId, chatId,
@ -25,6 +26,7 @@ export const useChatIndividualContext = ({
chatId, chatId,
defaultSelectedFile defaultSelectedFile
}); });
const hasChat = !!chatId && !!chat; const hasChat = !!chatId && !!chat;
const chatTitle = chat?.title; const chatTitle = chat?.title;
const chatMessageIds = chat?.messages ?? []; const chatMessageIds = chat?.messages ?? [];
@ -34,10 +36,11 @@ export const useChatIndividualContext = ({
//MESSAGES //MESSAGES
const currentMessageId = chatMessageIds[chatMessageIds.length - 1]; const currentMessageId = chatMessageIds[chatMessageIds.length - 1];
const isNewChat = chat?.isNewChat ?? false; const isLoading = useBusterChatContextSelector(
const isFollowUpChat = chat?.isFollowupMessage ?? false; (x) => x.chatsMessages[currentMessageId]?.isCompletedStream
);
useAutoChangeLayout({ lastMessageId: currentMessageId, onSetSelectedFile, chat }); useAutoChangeLayout({ lastMessageId: currentMessageId, onSetSelectedFile });
return { return {
hasChat, hasChat,
@ -48,8 +51,7 @@ export const useChatIndividualContext = ({
selectedFileType, selectedFileType,
chatMessageIds, chatMessageIds,
chatId, chatId,
isNewChat, isLoading
isFollowUpChat
}; };
}; };

View File

@ -1,24 +1,20 @@
import { IBusterChat, useBusterChatContextSelector } from '@/context/Chats'; import { useBusterChatContextSelector } from '@/context/Chats';
import { SelectedFile } from '../interfaces'; import type { SelectedFile } from '../interfaces';
import { usePrevious } from 'ahooks'; import { usePrevious } from 'ahooks';
import { useEffect } from 'react'; import { useEffect } from 'react';
export const useAutoChangeLayout = ({ export const useAutoChangeLayout = ({
lastMessageId, lastMessageId,
chat,
onSetSelectedFile onSetSelectedFile
}: { }: {
lastMessageId: string; lastMessageId: string;
chat: IBusterChat | undefined;
onSetSelectedFile: (file: SelectedFile) => void; onSetSelectedFile: (file: SelectedFile) => void;
}) => { }) => {
const message = useBusterChatContextSelector((x) => x.chatsMessages[lastMessageId]); const message = useBusterChatContextSelector((x) => x.chatsMessages[lastMessageId]);
const reasoningMessagesLength = message?.reasoning?.length; const reasoningMessagesLength = message?.reasoning?.length;
const previousReasoningMessagesLength = usePrevious(reasoningMessagesLength); const previousReasoningMessagesLength = usePrevious(reasoningMessagesLength);
const isCompletedStream = message?.isCompletedStream; const isCompletedStream = message?.isCompletedStream;
const isFollowupMessage = chat?.isFollowupMessage; const isLoading = !isCompletedStream;
const isNewChat = chat?.isNewChat;
const isLoading = isNewChat || isFollowupMessage || !isCompletedStream;
const hasReasoning = !!reasoningMessagesLength; const hasReasoning = !!reasoningMessagesLength;
const previousIsEmpty = previousReasoningMessagesLength === 0; const previousIsEmpty = previousReasoningMessagesLength === 0;

View File

@ -74,8 +74,6 @@ export const useFileFallback = ({
const fallbackToFileChat = ({ id }: { id: string }): IBusterChat => { const fallbackToFileChat = ({ id }: { id: string }): IBusterChat => {
return { return {
id, id,
isNewChat: false,
isFollowupMessage: false,
messages: [fallbackMessageId(id)], messages: [fallbackMessageId(id)],
title: '', title: '',
is_favorited: false, is_favorited: false,

View File

@ -5,7 +5,7 @@ import {
useContextSelector useContextSelector
} from '@fluentui/react-context-selector'; } from '@fluentui/react-context-selector';
import type { BusterChat } from '@/api/asset_interfaces'; import type { BusterChat } from '@/api/asset_interfaces';
import { IBusterChat, IBusterChatMessage } from '../interfaces'; import type { IBusterChat, IBusterChatMessage } from '../interfaces';
import { useChatSubscriptions } from './useChatSubscriptions'; import { useChatSubscriptions } from './useChatSubscriptions';
import { useChatAssosciations } from './useChatAssosciations'; import { useChatAssosciations } from './useChatAssosciations';
import { useChatSelectors } from './useChatSelectors'; import { useChatSelectors } from './useChatSelectors';

View File

@ -1 +1 @@
export * from './chatUpgrader'; export * from './useFileFallback';

View File

@ -61,8 +61,6 @@ export const useFileFallback = ({
const fallbackToFileChat = ({ id }: { id: string }): IBusterChat => { const fallbackToFileChat = ({ id }: { id: string }): IBusterChat => {
return { return {
id, id,
isNewChat: true,
isFollowupMessage: false,
messages: [fallbackMessageId(id)], messages: [fallbackMessageId(id)],
title: '', title: '',
is_favorited: false, is_favorited: false,

View File

@ -1,5 +1,5 @@
import { MutableRefObject, useCallback } from 'react'; import { type MutableRefObject, useCallback } from 'react';
import { IBusterChat, IBusterChatMessage } from '../interfaces'; import type { IBusterChat, IBusterChatMessage } from '../interfaces';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
export const useChatSelectors = ({ export const useChatSelectors = ({

View File

@ -2,8 +2,8 @@ import { MutableRefObject } from 'react';
import { useBusterWebSocket } from '../../BusterWebSocket'; import { useBusterWebSocket } from '../../BusterWebSocket';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import type { BusterChat } from '@/api/asset_interfaces'; import type { BusterChat } from '@/api/asset_interfaces';
import { IBusterChat, IBusterChatMessage } from '../interfaces'; import type { IBusterChat, IBusterChatMessage } from '../interfaces';
import { chatMessageUpgrader, chatUpgrader } from './helpers'; import { updateChatToIChat } from '@/utils/chat';
import { MOCK_CHAT } from './MOCK_CHAT'; import { MOCK_CHAT } from './MOCK_CHAT';
export const useChatSubscriptions = ({ export const useChatSubscriptions = ({
@ -18,17 +18,17 @@ export const useChatSubscriptions = ({
const busterSocket = useBusterWebSocket(); const busterSocket = useBusterWebSocket();
const _onGetChat = useMemoizedFn((chat: BusterChat): IBusterChat => { const _onGetChat = useMemoizedFn((chat: BusterChat): IBusterChat => {
const upgradedChat = chatUpgrader(chat); const { iChat, iChatMessages } = updateChatToIChat(chat);
const upgradedChatMessages = chatMessageUpgrader(chat.messages);
chatsRef.current[chat.id] = upgradedChat; chatsRef.current[chat.id] = iChat;
chatsMessagesRef.current = { chatsMessagesRef.current = {
...chatsMessagesRef.current, ...chatsMessagesRef.current,
...upgradedChatMessages ...iChatMessages
}; };
startTransition(() => { startTransition(() => {
//just used to trigger UI update //just used to trigger UI update
}); });
return upgradedChat; return iChat;
}); });
const unsubscribeFromChat = useMemoizedFn(({ chatId }: { chatId: string }) => { const unsubscribeFromChat = useMemoizedFn(({ chatId }: { chatId: string }) => {

View File

@ -1,7 +1,7 @@
import { useBusterWebSocket } from '@/context/BusterWebSocket'; import { useBusterWebSocket } from '@/context/BusterWebSocket';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import { MutableRefObject } from 'react'; import { type MutableRefObject } from 'react';
import { IBusterChat, IBusterChatMessage } from '../interfaces'; import type { IBusterChat, IBusterChatMessage } from '../interfaces';
export const useChatUpdate = ({ export const useChatUpdate = ({
chatsRef, chatsRef,
@ -15,13 +15,25 @@ export const useChatUpdate = ({
const busterSocket = useBusterWebSocket(); const busterSocket = useBusterWebSocket();
const onUpdateChat = useMemoizedFn( const onUpdateChat = useMemoizedFn(
async (newChatConfig: Partial<IBusterChat> & { id: string }) => { async (newChatConfig: Partial<IBusterChat> & { id: string }, saveToServer: boolean = false) => {
chatsRef.current[newChatConfig.id] = { chatsRef.current[newChatConfig.id] = {
...chatsRef.current[newChatConfig.id], ...chatsRef.current[newChatConfig.id],
...newChatConfig ...newChatConfig
}; };
startTransition(() => { startTransition(() => {
//just used to trigger UI update //just used to trigger UI update
if (saveToServer) {
const { title, is_favorited, id } = chatsRef.current[newChatConfig.id];
busterSocket.emit({
route: '/chats/update',
payload: {
id,
title,
is_favorited
}
});
}
}); });
} }
); );
@ -38,8 +50,21 @@ export const useChatUpdate = ({
} }
); );
const onBulkSetChatMessages = useMemoizedFn(
(newMessagesConfig: Record<string, IBusterChatMessage>) => {
chatsMessagesRef.current = {
...chatsMessagesRef.current,
...newMessagesConfig
};
startTransition(() => {
//just used to trigger UI update
});
}
);
return { return {
onUpdateChat, onUpdateChat,
onUpdateChatMessage onUpdateChatMessage,
onBulkSetChatMessages
}; };
}; };

View File

@ -5,7 +5,7 @@ import {
useContextSelector useContextSelector
} 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 { BusterSearchResult, FileType } from '@/api/asset_interfaces';
import { useBusterWebSocket } from '@/context/BusterWebSocket'; import { useBusterWebSocket } from '@/context/BusterWebSocket';
import { useChatUpdateMessage } from './useChatUpdateMessage'; import { useChatUpdateMessage } from './useChatUpdateMessage';

View File

@ -7,6 +7,7 @@ import {
ChatEvent_GeneratingResponseMessage, ChatEvent_GeneratingResponseMessage,
ChatEvent_GeneratingTitle ChatEvent_GeneratingTitle
} from '@/api/buster_socket/chats'; } from '@/api/buster_socket/chats';
import { updateChatToIChat } from '@/utils/chat';
export const useChatUpdateMessage = () => { export const useChatUpdateMessage = () => {
const busterSocket = useBusterWebSocket(); const busterSocket = useBusterWebSocket();
@ -14,6 +15,7 @@ export const useChatUpdateMessage = () => {
const getChatMemoized = useBusterChatContextSelector((x) => x.getChatMemoized); const getChatMemoized = useBusterChatContextSelector((x) => x.getChatMemoized);
const onUpdateChatMessage = useBusterChatContextSelector((x) => x.onUpdateChatMessage); const onUpdateChatMessage = useBusterChatContextSelector((x) => x.onUpdateChatMessage);
const getChatMessageMemoized = useBusterChatContextSelector((x) => x.getChatMessageMemoized); const getChatMessageMemoized = useBusterChatContextSelector((x) => x.getChatMessageMemoized);
const onBulkSetChatMessages = useBusterChatContextSelector((x) => x.onBulkSetChatMessages);
const _generatingTitleCallback = useMemoizedFn((d: ChatEvent_GeneratingTitle) => { const _generatingTitleCallback = useMemoizedFn((d: ChatEvent_GeneratingTitle) => {
const { chat_id, title, title_chunk } = d; const { chat_id, title, title_chunk } = d;
@ -57,10 +59,9 @@ export const useChatUpdateMessage = () => {
); );
const completeChatCallback = useMemoizedFn((d: BusterChat) => { const completeChatCallback = useMemoizedFn((d: BusterChat) => {
onUpdateChatMessage({ const { iChat, iChatMessages } = updateChatToIChat(d);
...d, onBulkSetChatMessages(iChatMessages);
isCompletedStream: true onUpdateChat(iChat);
});
}); });
const stopChatCallback = useMemoizedFn((chatId: string) => { const stopChatCallback = useMemoizedFn((chatId: string) => {

View File

@ -1,8 +1,6 @@
import type { BusterChat, BusterChatMessage } from '@/api/asset_interfaces'; import type { BusterChat, BusterChatMessage } from '@/api/asset_interfaces';
export interface IBusterChat extends Omit<BusterChat, 'messages'> { export interface IBusterChat extends Omit<BusterChat, 'messages'> {
isNewChat: boolean;
isFollowupMessage: boolean;
messages: string[]; messages: string[];
} }

View File

@ -1,20 +1,14 @@
import type { BusterChat, BusterChatMessage } from '@/api/asset_interfaces'; import type { BusterChat, BusterChatMessage } from '@/api/asset_interfaces';
import type { IBusterChat, IBusterChatMessage } from '../../interfaces'; import type { IBusterChat, IBusterChatMessage } from '@/context/Chats/interfaces';
export const chatUpgrader = ( const chatUpgrader = (chat: BusterChat): IBusterChat => {
chat: BusterChat,
options?: { isNewChat?: boolean; isFollowupMessage?: boolean }
): IBusterChat => {
const { isNewChat = false, isFollowupMessage = false } = options || {};
return { return {
...chat, ...chat,
isNewChat,
isFollowupMessage,
messages: chat.messages.map((message) => message.id) messages: chat.messages.map((message) => message.id)
}; };
}; };
export const chatMessageUpgrader = ( const chatMessageUpgrader = (
message: BusterChatMessage[], message: BusterChatMessage[],
options?: { isCompletedStream: boolean; messageId: string } options?: { isCompletedStream: boolean; messageId: string }
): Record<string, IBusterChatMessage> => { ): Record<string, IBusterChatMessage> => {
@ -40,3 +34,12 @@ export const chatMessageUpgrader = (
{} as Record<string, IBusterChatMessage> {} as Record<string, IBusterChatMessage>
); );
}; };
export const updateChatToIChat = (chat: BusterChat) => {
const iChat = chatUpgrader(chat);
const iChatMessages = chatMessageUpgrader(chat.messages);
return {
iChat,
iChatMessages
};
};