mirror of https://github.com/buster-so/buster.git
upate replace message logic
This commit is contained in:
parent
ab58050270
commit
bc2e126eb8
|
@ -3,7 +3,7 @@ import type { FileType, ThoughtFileType } from './config';
|
||||||
|
|
||||||
export type BusterChatMessage = {
|
export type BusterChatMessage = {
|
||||||
id: string;
|
id: string;
|
||||||
request_message: BusterChatMessageRequest;
|
request_message: BusterChatMessageRequest | null;
|
||||||
response_message_ids: string[];
|
response_message_ids: string[];
|
||||||
response_messages: Record<string, BusterChatMessageResponse>;
|
response_messages: Record<string, BusterChatMessageResponse>;
|
||||||
reasoning_message_ids: string[];
|
reasoning_message_ids: string[];
|
||||||
|
|
|
@ -153,7 +153,7 @@ export const useDeleteChat = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetChatMemoized = () => {
|
export const useGetChatMessageMemoized = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const getChatMessageMemoized = useMemoizedFn((messageId: string) => {
|
const getChatMessageMemoized = useMemoizedFn((messageId: string) => {
|
||||||
|
@ -165,6 +165,18 @@ export const useGetChatMemoized = () => {
|
||||||
return getChatMessageMemoized;
|
return getChatMessageMemoized;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetChatMemoized = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const getChatMemoized = useMemoizedFn((chatId: string) => {
|
||||||
|
const options = queryKeys.chatsGetChat(chatId);
|
||||||
|
const queryKey = options.queryKey;
|
||||||
|
return queryClient.getQueryData<IBusterChat>(queryKey);
|
||||||
|
});
|
||||||
|
|
||||||
|
return getChatMemoized;
|
||||||
|
};
|
||||||
|
|
||||||
export const useGetChatMessage = <TData = IBusterChatMessage>(
|
export const useGetChatMessage = <TData = IBusterChatMessage>(
|
||||||
messageId: string,
|
messageId: string,
|
||||||
selector?: (message: IBusterChatMessage) => TData
|
selector?: (message: IBusterChatMessage) => TData
|
||||||
|
|
|
@ -6,16 +6,18 @@ import { useMemoizedFn } from '@/hooks';
|
||||||
import type { 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 { useChatStreamMessage } from './useChatStreamMessage';
|
import { useChatStreamMessage } from './useChatStreamMessage';
|
||||||
|
import { useGetChatMemoized, useGetChatMessageMemoized } from '@/api/buster_rest/chats';
|
||||||
|
import { useChatUpdate } from './useChatUpdate';
|
||||||
|
import { create } from 'mutative';
|
||||||
|
|
||||||
export const useBusterNewChat = () => {
|
export const useBusterNewChat = () => {
|
||||||
const busterSocket = useBusterWebSocket();
|
const busterSocket = useBusterWebSocket();
|
||||||
|
const getChatMessageMemoized = useGetChatMessageMemoized();
|
||||||
|
const getChatMemoized = useGetChatMemoized();
|
||||||
|
const { onUpdateChat, onUpdateChatMessage } = useChatUpdate();
|
||||||
|
|
||||||
const {
|
const { completeChatCallback, stopChatCallback, initializeNewChatCallback } =
|
||||||
completeChatCallback,
|
useChatStreamMessage();
|
||||||
stopChatCallback,
|
|
||||||
initializeNewChatCallback,
|
|
||||||
replaceMessageCallback
|
|
||||||
} = useChatStreamMessage();
|
|
||||||
|
|
||||||
const onSelectSearchAsset = useMemoizedFn(async (asset: BusterSearchResult) => {
|
const onSelectSearchAsset = useMemoizedFn(async (asset: BusterSearchResult) => {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
|
@ -84,10 +86,31 @@ export const useBusterNewChat = () => {
|
||||||
messageId: string;
|
messageId: string;
|
||||||
chatId: string;
|
chatId: string;
|
||||||
}) => {
|
}) => {
|
||||||
replaceMessageCallback({
|
const currentChat = getChatMemoized(chatId);
|
||||||
prompt,
|
const currentMessage = getChatMessageMemoized(messageId);
|
||||||
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
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await busterSocket.emitAndOnce({
|
await busterSocket.emitAndOnce({
|
||||||
emitEvent: {
|
emitEvent: {
|
||||||
route: '/chats/post',
|
route: '/chats/post',
|
||||||
|
|
|
@ -9,11 +9,11 @@ import { IBusterChatMessage } from '@/api/asset_interfaces/chat';
|
||||||
import { ChatEvent_GeneratingReasoningMessage } from '@/api/buster_socket/chats';
|
import { ChatEvent_GeneratingReasoningMessage } from '@/api/buster_socket/chats';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { queryKeys } from '@/api/query_keys';
|
import { queryKeys } from '@/api/query_keys';
|
||||||
import { useGetChatMemoized } from '@/api/buster_rest/chats';
|
import { useGetChatMessageMemoized } from '@/api/buster_rest/chats';
|
||||||
|
|
||||||
export const useBlackBoxMessage = () => {
|
export const useBlackBoxMessage = () => {
|
||||||
const timeoutRef = useRef<Record<string, ReturnType<typeof setTimeout>>>({});
|
const timeoutRef = useRef<Record<string, ReturnType<typeof setTimeout>>>({});
|
||||||
const getChatMessageMemoized = useGetChatMemoized();
|
const getChatMessageMemoized = useGetChatMessageMemoized();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const clearTimeoutRef = useMemoizedFn((messageId: string) => {
|
const clearTimeoutRef = useMemoizedFn((messageId: string) => {
|
||||||
|
|
|
@ -21,14 +21,12 @@ import {
|
||||||
updateResponseMessage,
|
updateResponseMessage,
|
||||||
updateReasoningMessage
|
updateReasoningMessage
|
||||||
} from './chatStreamMessageHelper';
|
} from './chatStreamMessageHelper';
|
||||||
import { useGetChatMemoized } from '@/api/buster_rest/chats';
|
|
||||||
import { useChatUpdate } from './useChatUpdate';
|
import { useChatUpdate } from './useChatUpdate';
|
||||||
import { prefetchGetMetricDataClient, prefetchGetMetric } from '@/api/buster_rest/metrics';
|
import { prefetchGetMetricDataClient } from '@/api/buster_rest/metrics';
|
||||||
|
|
||||||
export const useChatStreamMessage = () => {
|
export const useChatStreamMessage = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const onChangePage = useAppLayoutContextSelector((x) => x.onChangePage);
|
const onChangePage = useAppLayoutContextSelector((x) => x.onChangePage);
|
||||||
const getChatMessageMemoized = useGetChatMemoized();
|
|
||||||
const { onUpdateChat, onUpdateChatMessage } = useChatUpdate();
|
const { onUpdateChat, onUpdateChatMessage } = useChatUpdate();
|
||||||
const chatRef = useRef<Record<string, IBusterChat>>({});
|
const chatRef = useRef<Record<string, IBusterChat>>({});
|
||||||
const chatRefMessages = useRef<Record<string, IBusterChatMessage>>({});
|
const chatRefMessages = useRef<Record<string, IBusterChatMessage>>({});
|
||||||
|
@ -110,21 +108,6 @@ export const useChatStreamMessage = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const replaceMessageCallback = useMemoizedFn(
|
|
||||||
({ prompt, messageId }: { prompt: string; messageId: string }) => {
|
|
||||||
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: []
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const _generatingTitleCallback = useMemoizedFn((_: null, newData: ChatEvent_GeneratingTitle) => {
|
const _generatingTitleCallback = useMemoizedFn((_: null, newData: ChatEvent_GeneratingTitle) => {
|
||||||
const { chat_id } = newData;
|
const { chat_id } = newData;
|
||||||
const currentChat = chatRef.current[chat_id];
|
const currentChat = chatRef.current[chat_id];
|
||||||
|
@ -189,7 +172,6 @@ export const useChatStreamMessage = () => {
|
||||||
return {
|
return {
|
||||||
initializeNewChatCallback,
|
initializeNewChatCallback,
|
||||||
completeChatCallback,
|
completeChatCallback,
|
||||||
stopChatCallback,
|
stopChatCallback
|
||||||
replaceMessageCallback
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,14 +7,22 @@ export const ChatMessageBlock: React.FC<{
|
||||||
messageId: string;
|
messageId: string;
|
||||||
chatId: string;
|
chatId: string;
|
||||||
}> = React.memo(({ messageId, chatId }) => {
|
}> = React.memo(({ messageId, chatId }) => {
|
||||||
|
const messageExists = useGetChatMessage(messageId, (message) => message?.id);
|
||||||
const requestMessage = useGetChatMessage(messageId, (message) => message?.request_message);
|
const requestMessage = useGetChatMessage(messageId, (message) => message?.request_message);
|
||||||
const isCompletedStream = useGetChatMessage(messageId, (x) => x?.isCompletedStream);
|
const isCompletedStream = useGetChatMessage(messageId, (x) => x?.isCompletedStream);
|
||||||
|
|
||||||
if (!requestMessage) return null;
|
if (!messageExists) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'flex flex-col space-y-3.5 py-2 pr-3 pl-4'} id={messageId}>
|
<div className={'flex flex-col space-y-3.5 py-2 pr-3 pl-4'} id={messageId}>
|
||||||
<ChatUserMessage requestMessage={requestMessage} />
|
{requestMessage && (
|
||||||
|
<ChatUserMessage
|
||||||
|
isCompletedStream={isCompletedStream!}
|
||||||
|
chatId={chatId}
|
||||||
|
messageId={messageId}
|
||||||
|
requestMessage={requestMessage}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<ChatResponseMessages
|
<ChatResponseMessages
|
||||||
isCompletedStream={isCompletedStream!}
|
isCompletedStream={isCompletedStream!}
|
||||||
messageId={messageId}
|
messageId={messageId}
|
||||||
|
|
|
@ -1,20 +1,145 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
import type { BusterChatMessageRequest } from '@/api/asset_interfaces';
|
import type { BusterChatMessageRequest } from '@/api/asset_interfaces';
|
||||||
import React from 'react';
|
import React, { useState, useRef } from 'react';
|
||||||
import { Paragraph } from '@/components/ui/typography';
|
import { Paragraph } from '@/components/ui/typography';
|
||||||
import { MessageContainer } from './MessageContainer';
|
import { MessageContainer } from './MessageContainer';
|
||||||
|
import { Tooltip } from '@/components/ui/tooltip';
|
||||||
|
import { cn } from '@/lib/classMerge';
|
||||||
|
import { PenWriting, Copy, Check } from '@/components/ui/icons';
|
||||||
|
import { Button } from '@/components/ui/buttons';
|
||||||
|
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||||
|
import { useMemoizedFn } from '@/hooks';
|
||||||
|
import { InputTextArea } from '@/components/ui/inputs/InputTextArea';
|
||||||
|
import { useBusterNewChatContextSelector } from '@/context/Chats';
|
||||||
|
|
||||||
export const ChatUserMessage: React.FC<{ requestMessage: BusterChatMessageRequest }> = React.memo(
|
export const ChatUserMessage: React.FC<{
|
||||||
({ requestMessage }) => {
|
messageId: string;
|
||||||
if (!requestMessage) return null;
|
chatId: string;
|
||||||
|
isCompletedStream: boolean;
|
||||||
|
requestMessage: NonNullable<BusterChatMessageRequest>;
|
||||||
|
}> = React.memo(({ messageId, chatId, isCompletedStream, requestMessage }) => {
|
||||||
|
const [isTooltipOpen, setIsTooltipOpen] = useState(false);
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
|
||||||
const { sender_avatar, sender_id, sender_name, request } = requestMessage;
|
const { sender_avatar, sender_id, sender_name, request } = requestMessage;
|
||||||
|
|
||||||
return (
|
const onSetIsEditing = useMemoizedFn((isEditing: boolean) => {
|
||||||
<MessageContainer senderName={sender_name} senderId={sender_id} senderAvatar={sender_avatar}>
|
setIsEditing(isEditing);
|
||||||
<Paragraph>{request}</Paragraph>
|
setIsTooltipOpen(false);
|
||||||
</MessageContainer>
|
});
|
||||||
);
|
|
||||||
}
|
return (
|
||||||
);
|
<MessageContainer
|
||||||
|
senderName={sender_name}
|
||||||
|
senderId={sender_id}
|
||||||
|
senderAvatar={sender_avatar}
|
||||||
|
onMouseEnter={() => setIsTooltipOpen(true)}
|
||||||
|
onMouseLeave={() => setIsTooltipOpen(false)}>
|
||||||
|
{isEditing ? (
|
||||||
|
<EditMessage
|
||||||
|
messageId={messageId}
|
||||||
|
chatId={chatId}
|
||||||
|
requestMessage={requestMessage}
|
||||||
|
onSetIsEditing={onSetIsEditing}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Paragraph>{request}</Paragraph>
|
||||||
|
{isCompletedStream && (
|
||||||
|
<RequestMessageTooltip
|
||||||
|
isTooltipOpen={isTooltipOpen}
|
||||||
|
requestMessage={requestMessage}
|
||||||
|
setIsEditing={setIsEditing}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</MessageContainer>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
ChatUserMessage.displayName = 'ChatUserMessage';
|
ChatUserMessage.displayName = 'ChatUserMessage';
|
||||||
|
|
||||||
|
const RequestMessageTooltip: React.FC<{
|
||||||
|
isTooltipOpen: boolean;
|
||||||
|
requestMessage: NonNullable<BusterChatMessageRequest>;
|
||||||
|
setIsEditing: (isEditing: boolean) => void;
|
||||||
|
}> = React.memo(({ isTooltipOpen, requestMessage, setIsEditing }) => {
|
||||||
|
const { openSuccessMessage } = useBusterNotifications();
|
||||||
|
|
||||||
|
const onCopy = useMemoizedFn(() => {
|
||||||
|
navigator.clipboard.writeText(requestMessage.request);
|
||||||
|
openSuccessMessage('Copied to clipboard');
|
||||||
|
});
|
||||||
|
|
||||||
|
const onEdit = useMemoizedFn(() => {
|
||||||
|
setIsEditing(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'absolute right-1 -bottom-0 translate-y-full transform',
|
||||||
|
'bg-background z-50 rounded border shadow',
|
||||||
|
'transition-all duration-200',
|
||||||
|
isTooltipOpen ? 'scale-100 opacity-100' : 'scale-95 opacity-0'
|
||||||
|
)}>
|
||||||
|
<Tooltip title={'Edit'} side={'bottom'}>
|
||||||
|
<Button
|
||||||
|
prefix={<PenWriting />}
|
||||||
|
className="hover:bg-item-select!"
|
||||||
|
variant={'ghost'}
|
||||||
|
onClick={onEdit}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title={'Copy'} side={'bottom'}>
|
||||||
|
<Button
|
||||||
|
prefix={<Copy />}
|
||||||
|
className="hover:bg-item-select!"
|
||||||
|
variant={'ghost'}
|
||||||
|
onClick={onCopy}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
RequestMessageTooltip.displayName = 'RequestMessageTooltip';
|
||||||
|
|
||||||
|
const EditMessage: React.FC<{
|
||||||
|
requestMessage: NonNullable<BusterChatMessageRequest>;
|
||||||
|
onSetIsEditing: (isEditing: boolean) => void;
|
||||||
|
messageId: string;
|
||||||
|
chatId: string;
|
||||||
|
}> = React.memo(({ requestMessage, onSetIsEditing, messageId, chatId }) => {
|
||||||
|
const [prompt, setPrompt] = useState(requestMessage.request);
|
||||||
|
const onReplaceMessageInChat = useBusterNewChatContextSelector((x) => x.onReplaceMessageInChat);
|
||||||
|
|
||||||
|
const onSave = useMemoizedFn((text: string) => {
|
||||||
|
onReplaceMessageInChat({
|
||||||
|
chatId,
|
||||||
|
messageId,
|
||||||
|
prompt
|
||||||
|
});
|
||||||
|
onSetIsEditing(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="-mt-1 flex flex-col space-y-2">
|
||||||
|
<InputTextArea
|
||||||
|
autoResize={{ minRows: 3, maxRows: 10 }}
|
||||||
|
value={prompt}
|
||||||
|
onChange={(e) => setPrompt(e.target.value)}
|
||||||
|
/>
|
||||||
|
<div className="flex justify-end space-x-2">
|
||||||
|
<Button variant={'ghost'} onClick={() => onSetIsEditing(false)}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button variant={'black'} onClick={() => onSave(prompt)}>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
|
@ -1,24 +1,37 @@
|
||||||
import { Avatar } from '@/components/ui/avatar';
|
import { Avatar } from '@/components/ui/avatar';
|
||||||
import { cn } from '@/lib/classMerge';
|
import { cn } from '@/lib/classMerge';
|
||||||
import React from 'react';
|
import React, { forwardRef } from 'react';
|
||||||
|
|
||||||
export const MessageContainer: React.FC<{
|
interface MessageContainerProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
senderName?: string;
|
senderName?: string;
|
||||||
senderId?: string;
|
senderId?: string;
|
||||||
senderAvatar?: string | null;
|
senderAvatar?: string | null;
|
||||||
className?: string;
|
className?: string;
|
||||||
}> = React.memo(({ children, senderName, senderId, senderAvatar, className = '' }) => {
|
onMouseEnter?: () => void;
|
||||||
return (
|
onMouseLeave?: () => void;
|
||||||
<div className={'flex w-full space-x-2 overflow-hidden'}>
|
}
|
||||||
{senderName ? (
|
|
||||||
<Avatar size={24} name={senderName} image={senderAvatar || ''} useToolTip={true} />
|
export const MessageContainer = forwardRef<HTMLDivElement, MessageContainerProps>(
|
||||||
) : (
|
(
|
||||||
<Avatar size={24} />
|
{ children, senderName, senderId, senderAvatar, className = '', onMouseEnter, onMouseLeave },
|
||||||
)}
|
ref
|
||||||
<div className={cn('mt-1 px-1', className)}>{children}</div>
|
) => {
|
||||||
</div>
|
return (
|
||||||
);
|
<div
|
||||||
});
|
ref={ref}
|
||||||
|
className={'flex w-full space-x-2'}
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
onMouseLeave={onMouseLeave}>
|
||||||
|
{senderName ? (
|
||||||
|
<Avatar size={24} name={senderName} image={senderAvatar || ''} useToolTip={true} />
|
||||||
|
) : (
|
||||||
|
<Avatar size={24} />
|
||||||
|
)}
|
||||||
|
<div className={cn('relative mt-1 w-full px-1', className)}>{children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
MessageContainer.displayName = 'MessageContainer';
|
MessageContainer.displayName = 'MessageContainer';
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useGetChatMemoized, useGetChatMessage } from '@/api/buster_rest/chats';
|
import { useGetChatMessageMemoized, useGetChatMessage } from '@/api/buster_rest/chats';
|
||||||
import type { SelectedFile } from '../interfaces';
|
import type { SelectedFile } from '../interfaces';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import findLast from 'lodash/findLast';
|
import findLast from 'lodash/findLast';
|
||||||
import { BusterChatResponseMessage_file } from '@/api/asset_interfaces/chat';
|
import { BusterChatResponseMessage_file } from '@/api/asset_interfaces/chat';
|
||||||
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
|
||||||
import { BusterRoutes } from '@/routes';
|
|
||||||
|
|
||||||
export const useAutoChangeLayout = ({
|
export const useAutoChangeLayout = ({
|
||||||
lastMessageId,
|
lastMessageId,
|
||||||
|
@ -24,7 +22,7 @@ export const useAutoChangeLayout = ({
|
||||||
lastMessageId,
|
lastMessageId,
|
||||||
(x) => x?.reasoning_message_ids?.length || 0
|
(x) => x?.reasoning_message_ids?.length || 0
|
||||||
);
|
);
|
||||||
const getChatMessageMemoized = useGetChatMemoized();
|
const getChatMessageMemoized = useGetChatMessageMemoized();
|
||||||
|
|
||||||
const isCompletedStream = useGetChatMessage(lastMessageId, (x) => x?.isCompletedStream);
|
const isCompletedStream = useGetChatMessage(lastMessageId, (x) => x?.isCompletedStream);
|
||||||
const hasReasoning = !!reasoningMessagesLength;
|
const hasReasoning = !!reasoningMessagesLength;
|
||||||
|
|
Loading…
Reference in New Issue