mirror of https://github.com/buster-so/buster.git
reasoning file click
This commit is contained in:
parent
75873c2ef5
commit
4ef095ceba
|
@ -6,7 +6,7 @@ import type { IBusterChatMessage } from '@/context/Chats/interfaces';
|
|||
export const ChatMessageBlock: React.FC<{
|
||||
message: IBusterChatMessage;
|
||||
}> = React.memo(({ message }) => {
|
||||
const { request_message, response_messages, id, isCompletedStream } = message;
|
||||
const { request_message, response_messages, id, isCompletedStream, reasoning } = message;
|
||||
|
||||
return (
|
||||
<div className={'flex flex-col space-y-3.5 py-2 pl-4 pr-3'} id={id}>
|
||||
|
@ -14,6 +14,8 @@ export const ChatMessageBlock: React.FC<{
|
|||
<ChatResponseMessages
|
||||
responseMessages={response_messages}
|
||||
isCompletedStream={isCompletedStream}
|
||||
reasoningMessages={reasoning}
|
||||
messageId={id}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { ChatResponseMessage_File } from './ChatResponseMessage_File';
|
||||
import { StreamingMessage_Text } from '@appComponents/Streaming/StreamingMessage_Text';
|
||||
import type { BusterChatMessage_text, BusterChatMessageResponse } from '@/api/asset_interfaces';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
||||
export interface ChatResponseMessageProps {
|
||||
responseMessage: BusterChatMessageResponse;
|
||||
|
@ -32,11 +34,74 @@ export const ChatResponseMessageSelector: React.FC<ChatResponseMessageSelectorPr
|
|||
}) => {
|
||||
const messageType = responseMessage.type;
|
||||
const ChatResponseMessage = ChatResponseMessageRecord[messageType];
|
||||
const { cx, styles } = useStyles();
|
||||
|
||||
const typeClassRecord: Record<BusterChatMessageResponse['type'], string> = useMemo(() => {
|
||||
return {
|
||||
text: cx(styles.textCard, 'text-card'),
|
||||
file: cx(styles.fileCard, 'file-card')
|
||||
};
|
||||
}, []);
|
||||
|
||||
const getContainerClass = useMemoizedFn((item: BusterChatMessageResponse) => {
|
||||
return typeClassRecord[item.type];
|
||||
});
|
||||
|
||||
return (
|
||||
<ChatResponseMessage
|
||||
responseMessage={responseMessage}
|
||||
isCompletedStream={isCompletedStream}
|
||||
isLastMessageItem={isLastMessageItem}
|
||||
/>
|
||||
<div key={responseMessage.id} className={getContainerClass(responseMessage)}>
|
||||
<ChatResponseMessage
|
||||
responseMessage={responseMessage}
|
||||
isCompletedStream={isCompletedStream}
|
||||
isLastMessageItem={isLastMessageItem}
|
||||
/>
|
||||
<VerticalDivider />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const VerticalDivider: React.FC<{ className?: string }> = React.memo(({ className }) => {
|
||||
const { cx, styles } = useStyles();
|
||||
return <div className={cx(styles.verticalDivider, 'vertical-divider', className)} />;
|
||||
});
|
||||
VerticalDivider.displayName = 'VerticalDivider';
|
||||
|
||||
const useStyles = createStyles(({ token, css }) => ({
|
||||
textCard: css`
|
||||
margin-bottom: 14px;
|
||||
|
||||
&:has(+ .text-card) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.vertical-divider {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
fileCard: css`
|
||||
&:has(+ .text-card) {
|
||||
.vertical-divider {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:has(+ .file-card) {
|
||||
.vertical-divider {
|
||||
opacity: 1;
|
||||
}
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.vertical-divider {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
`,
|
||||
verticalDivider: css`
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
height: 9px;
|
||||
width: 0.5px;
|
||||
margin: 3px 0 3px 16px;
|
||||
background: ${token.colorTextTertiary};
|
||||
`
|
||||
}));
|
||||
|
|
|
@ -1,46 +1,56 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import type { BusterChatMessageResponse } from '@/api/asset_interfaces';
|
||||
import type {
|
||||
BusterChatMessage_text,
|
||||
BusterChatMessageReasoning,
|
||||
BusterChatMessageResponse
|
||||
} from '@/api/asset_interfaces';
|
||||
import { MessageContainer } from '../MessageContainer';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { ChatResponseMessageSelector } from './ChatResponseMessageSelector';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { ChatResponseReasoning } from './ChatResponseReasoning';
|
||||
|
||||
interface ChatResponseMessagesProps {
|
||||
responseMessages: BusterChatMessageResponse[];
|
||||
isCompletedStream: boolean;
|
||||
reasoningMessages: BusterChatMessageReasoning[];
|
||||
messageId: string;
|
||||
}
|
||||
|
||||
export const ChatResponseMessages: React.FC<ChatResponseMessagesProps> = React.memo(
|
||||
({ responseMessages, isCompletedStream }) => {
|
||||
const { styles, cx } = useStyles();
|
||||
|
||||
const firstResponseMessage = responseMessages[0];
|
||||
const restResponseMessages = responseMessages.slice(1);
|
||||
({ responseMessages, reasoningMessages, isCompletedStream, messageId }) => {
|
||||
const firstResponseMessage = responseMessages[0] as BusterChatMessage_text;
|
||||
const restResponseMessages = useMemo(() => {
|
||||
if (!firstResponseMessage) return [];
|
||||
return responseMessages.slice(1);
|
||||
}, [firstResponseMessage, responseMessages]);
|
||||
|
||||
const lastMessageIndex = responseMessages.length - 1;
|
||||
|
||||
const typeClassRecord: Record<BusterChatMessageResponse['type'], string> = useMemo(() => {
|
||||
return {
|
||||
text: cx(styles.textCard, 'text-card'),
|
||||
file: cx(styles.fileCard, 'file-card')
|
||||
};
|
||||
}, []);
|
||||
|
||||
const getContainerClass = useMemoizedFn((item: BusterChatMessageResponse) => {
|
||||
return typeClassRecord[item.type];
|
||||
});
|
||||
|
||||
return (
|
||||
<MessageContainer className="flex w-full flex-col overflow-hidden">
|
||||
{responseMessages.map((responseMessage, index) => (
|
||||
<div key={responseMessage.id} className={getContainerClass(responseMessage)}>
|
||||
<ChatResponseMessageSelector
|
||||
responseMessage={responseMessage}
|
||||
isCompletedStream={isCompletedStream}
|
||||
isLastMessageItem={index === lastMessageIndex}
|
||||
/>
|
||||
<VerticalDivider />
|
||||
</div>
|
||||
{firstResponseMessage && (
|
||||
<ChatResponseMessageSelector
|
||||
key={firstResponseMessage.id}
|
||||
responseMessage={firstResponseMessage}
|
||||
isCompletedStream={isCompletedStream}
|
||||
isLastMessageItem={false}
|
||||
/>
|
||||
)}
|
||||
|
||||
{firstResponseMessage && (
|
||||
<ChatResponseReasoning
|
||||
reasoningMessages={reasoningMessages}
|
||||
isCompletedStream={isCompletedStream}
|
||||
messageId={messageId}
|
||||
/>
|
||||
)}
|
||||
|
||||
{restResponseMessages.map((responseMessage, index) => (
|
||||
<ChatResponseMessageSelector
|
||||
key={responseMessage.id}
|
||||
responseMessage={responseMessage}
|
||||
isCompletedStream={isCompletedStream}
|
||||
isLastMessageItem={index === lastMessageIndex}
|
||||
/>
|
||||
))}
|
||||
</MessageContainer>
|
||||
);
|
||||
|
@ -48,52 +58,3 @@ export const ChatResponseMessages: React.FC<ChatResponseMessagesProps> = React.m
|
|||
);
|
||||
|
||||
ChatResponseMessages.displayName = 'ChatResponseMessages';
|
||||
|
||||
const VerticalDivider: React.FC<{ className?: string }> = React.memo(({ className }) => {
|
||||
const { cx, styles } = useStyles();
|
||||
return <div className={cx(styles.verticalDivider, 'vertical-divider', className)} />;
|
||||
});
|
||||
VerticalDivider.displayName = 'VerticalDivider';
|
||||
|
||||
const useStyles = createStyles(({ token, css }) => ({
|
||||
textCard: css`
|
||||
margin-bottom: 14px;
|
||||
|
||||
&:has(+ .text-card) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.vertical-divider {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
fileCard: css`
|
||||
&:has(+ .text-card),
|
||||
&:has(+ .hidden-card) {
|
||||
.vertical-divider {
|
||||
opacity: 0;
|
||||
}
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
&:has(+ .thought-card) {
|
||||
.vertical-divider {
|
||||
opacity: 0;
|
||||
}
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.vertical-divider {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
`,
|
||||
verticalDivider: css`
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
height: 9px;
|
||||
width: 0.5px;
|
||||
margin: 3px 0 3px 16px;
|
||||
background: ${token.colorTextTertiary};
|
||||
`
|
||||
}));
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
import { BusterChatMessageReasoning } from '@/api/asset_interfaces';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import last from 'lodash/last';
|
||||
import { ShimmerText } from '@/components/text';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { motion } from 'framer-motion';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import { AppMaterialIcons, Text } from '@/components';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { useChatLayoutContextSelector } from '../../../ChatLayoutContext';
|
||||
|
||||
export const ChatResponseReasoning: React.FC<{
|
||||
reasoningMessages: BusterChatMessageReasoning[];
|
||||
isCompletedStream: boolean;
|
||||
messageId: string;
|
||||
}> = React.memo(({ reasoningMessages, isCompletedStream, messageId }) => {
|
||||
const lastMessage = last(reasoningMessages);
|
||||
const onSetSelectedFile = useChatLayoutContextSelector((x) => x.onSetSelectedFile);
|
||||
const selectedFileType = useChatLayoutContextSelector((x) => x.selectedFileType);
|
||||
const isReasonginFileSelected = selectedFileType === 'reasoning';
|
||||
|
||||
const text = useMemo(() => {
|
||||
if (!lastMessage) return null;
|
||||
if (lastMessage.type === 'text') {
|
||||
return lastMessage.message;
|
||||
}
|
||||
return lastMessage.thought_title;
|
||||
}, [lastMessage]);
|
||||
|
||||
const getRandomThought = useMemoizedFn(() => {
|
||||
return DEFAULT_THOUGHTS[Math.floor(Math.random() * DEFAULT_THOUGHTS.length)];
|
||||
});
|
||||
|
||||
const onClickReasoning = useMemoizedFn(() => {
|
||||
onSetSelectedFile({
|
||||
type: 'reasoning',
|
||||
id: messageId
|
||||
});
|
||||
});
|
||||
|
||||
const [thought, setThought] = useState(text || DEFAULT_THOUGHTS[0]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCompletedStream && !text) {
|
||||
const randomInterval = Math.floor(Math.random() * 3000) + 1200;
|
||||
const interval = setTimeout(() => {
|
||||
setThought(getRandomThought());
|
||||
}, randomInterval);
|
||||
return () => clearTimeout(interval);
|
||||
}
|
||||
if (text) {
|
||||
setThought(text);
|
||||
}
|
||||
}, [thought, isCompletedStream, text, getRandomThought]);
|
||||
|
||||
const animations = useMemo(() => {
|
||||
return {
|
||||
initial: { opacity: 0 },
|
||||
animate: { opacity: 1 },
|
||||
exit: { opacity: 0 }
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AnimatePresence initial={!isCompletedStream} mode="wait">
|
||||
<motion.div {...animations} key={thought} className="mb-3.5 w-fit" onClick={onClickReasoning}>
|
||||
<ShimmerTextWithIcon
|
||||
text={thought}
|
||||
isCompletedStream={isCompletedStream}
|
||||
isSelected={isReasonginFileSelected}
|
||||
/>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
);
|
||||
});
|
||||
|
||||
ChatResponseReasoning.displayName = 'ChatThoughts';
|
||||
|
||||
const DEFAULT_THOUGHTS = [
|
||||
'Thinking through next steps...',
|
||||
'Looking through context...',
|
||||
'Reflecting on the instructions...',
|
||||
'Analyzing available actions',
|
||||
'Reviewing the objective...',
|
||||
'Deciding feasible options...',
|
||||
'Sorting out some details...',
|
||||
'Exploring other possibilities...',
|
||||
'Confirming things....',
|
||||
'Mapping information across files...',
|
||||
'Making a few edits...',
|
||||
'Filling out arguments...',
|
||||
'Double-checking the logic...',
|
||||
'Validating my approach...',
|
||||
'Looking at a few edge cases...',
|
||||
'Ensuring everything aligns...',
|
||||
'Polishing the details...',
|
||||
'Making some adjustments...',
|
||||
'Writing out arguments...',
|
||||
'Mapping trends and patterns...',
|
||||
'Re-evaluating this step...',
|
||||
'Updating parameters...',
|
||||
'Evaluating available data...',
|
||||
'Reviewing all parameters...',
|
||||
'Processing relevant info...',
|
||||
'Aligning with user request...',
|
||||
'Gathering necessary details...',
|
||||
'Sorting through options...',
|
||||
'Editing my system logic...',
|
||||
'Cross-checking references...',
|
||||
'Validating my approach...',
|
||||
'Rewriting operational details...',
|
||||
'Mapping new information...',
|
||||
'Adjusting priorities & approach...',
|
||||
'Revisiting earlier inputs...',
|
||||
'Finalizing plan details...'
|
||||
];
|
||||
|
||||
const ShimmerTextWithIcon = React.memo(
|
||||
({
|
||||
text,
|
||||
isCompletedStream,
|
||||
isSelected
|
||||
}: {
|
||||
text: string;
|
||||
isCompletedStream: boolean;
|
||||
isSelected: boolean;
|
||||
}) => {
|
||||
const { cx, styles } = useStyles();
|
||||
|
||||
if (isCompletedStream) {
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
styles.iconContainerCompleted,
|
||||
styles.iconContainer,
|
||||
'flex w-fit items-center gap-1',
|
||||
isSelected && 'is-selected'
|
||||
)}>
|
||||
<div>
|
||||
<AppMaterialIcons icon="stars" />
|
||||
</div>
|
||||
<Text type="inherit">{text}</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cx(styles.iconContainer, 'flex items-center gap-1')}>
|
||||
<div className={cx(styles.icon)}>
|
||||
<AppMaterialIcons icon="stars" />
|
||||
</div>
|
||||
<ShimmerText text={text} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
ShimmerTextWithIcon.displayName = 'ShimmerTextWithIcon';
|
||||
|
||||
const useStyles = createStyles(({ token, css }) => ({
|
||||
iconContainerCompleted: css`
|
||||
color: ${token.colorIcon};
|
||||
&:hover {
|
||||
color: ${token.colorText};
|
||||
}
|
||||
&.is-selected {
|
||||
color: ${token.colorText};
|
||||
}
|
||||
`,
|
||||
iconContainer: css`
|
||||
cursor: pointer;
|
||||
`,
|
||||
icon: css`
|
||||
color: ${token.colorIcon};
|
||||
`
|
||||
}));
|
|
@ -1,72 +0,0 @@
|
|||
import { BusterChatMessageReasoning } from '@/api/asset_interfaces';
|
||||
import React, { useMemo } from 'react';
|
||||
import last from 'lodash/last';
|
||||
import { ShimmerText } from '@/components/text';
|
||||
|
||||
export const ChatThoughts: React.FC<{
|
||||
reasoningMessages: BusterChatMessageReasoning[];
|
||||
}> = React.memo(({ reasoningMessages }) => {
|
||||
const lastMessage = last(reasoningMessages);
|
||||
|
||||
const lastMessageTest = useMemo(() => {
|
||||
if (!lastMessage) return null;
|
||||
if (lastMessage.type === 'text') {
|
||||
return lastMessage;
|
||||
}
|
||||
return lastMessage.thought_title;
|
||||
}, [lastMessage]);
|
||||
|
||||
const hasLastMessage = !!lastMessage || !!lastMessageTest;
|
||||
|
||||
if (!hasLastMessage) return null;
|
||||
|
||||
return <div>ChatThoughts</div>;
|
||||
});
|
||||
|
||||
ChatThoughts.displayName = 'ChatThoughts';
|
||||
|
||||
const DEFAULT_THOUGHTS = [
|
||||
'Thinking through next steps...',
|
||||
'Looking through context...',
|
||||
'Reflecting on the instructions...',
|
||||
'Analyzing available actions',
|
||||
'Reviewing the objective...',
|
||||
'Deciding feasible options...',
|
||||
'Sorting out some details...',
|
||||
'Exploring other possibilities...',
|
||||
'Confirming things....',
|
||||
'Mapping information across files...',
|
||||
'Making a few edits...',
|
||||
'Filling out arguments...',
|
||||
'Double-checking the logic...',
|
||||
'Validating my approach...',
|
||||
'Looking at a few edge cases...',
|
||||
'Ensuring everything aligns...',
|
||||
'Polishing the details...',
|
||||
'Making some adjustments...',
|
||||
'Writing out arguments...',
|
||||
'Mapping trends and patterns...',
|
||||
'Re-evaluating this step...',
|
||||
'Updating parameters...',
|
||||
'Evaluating available data...',
|
||||
'Reviewing all parameters...',
|
||||
'Processing relevant info...',
|
||||
'Aligning with user request...',
|
||||
'Gathering necessary details...',
|
||||
'Sorting through options...',
|
||||
'Editing my system logic...',
|
||||
'Cross-checking references...',
|
||||
'Validating my approach...',
|
||||
'Rewriting operational details...',
|
||||
'Mapping new information...',
|
||||
'Adjusting priorities & approach...',
|
||||
'Revisiting earlier inputs...',
|
||||
'Finalizing plan details...'
|
||||
];
|
||||
|
||||
const RandomThoughts = React.memo(() => {
|
||||
const randomThought = DEFAULT_THOUGHTS[Math.floor(Math.random() * DEFAULT_THOUGHTS.length)];
|
||||
return <div>{randomThought}</div>;
|
||||
});
|
||||
|
||||
RandomThoughts.displayName = 'RandomThoughts';
|
|
@ -3,7 +3,7 @@ import {
|
|||
createContext,
|
||||
useContextSelector
|
||||
} from '@fluentui/react-context-selector';
|
||||
import React, { PropsWithChildren, useState, useTransition } from 'react';
|
||||
import React, { PropsWithChildren, useTransition } from 'react';
|
||||
import type { SelectedFile } from '../interfaces';
|
||||
import type { ChatSplitterProps } from '../ChatLayout';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
@ -51,7 +51,7 @@ export const useChatLayout = ({
|
|||
const fileType = file.type;
|
||||
const fileId = file.id;
|
||||
const route =
|
||||
isChatView && chatId
|
||||
isChatView && chatId !== undefined
|
||||
? createChatAssetRoute({ chatId, assetId: fileId, type: fileType })
|
||||
: createFileRoute({ assetId: fileId, type: fileType });
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type { ThoughtFileType, FileType } from '@/api/asset_interfaces';
|
||||
|
||||
export const isOpenableFile = (type: ThoughtFileType): type is FileType => {
|
||||
const validTypes: FileType[] = ['metric', 'dashboard'];
|
||||
return validTypes.includes(type as FileType);
|
||||
const OPENABLE_FILES = new Set<string>(['metric', 'dashboard', 'reasoning']);
|
||||
|
||||
export const isOpenableFile = (type: ThoughtFileType): boolean => {
|
||||
return OPENABLE_FILES.has(type);
|
||||
};
|
||||
|
|
|
@ -36,7 +36,5 @@ export const useSelectedFileByParams = () => {
|
|||
return 'chat';
|
||||
}, [metricId, collectionId, datasetId, dashboardId, chatId]);
|
||||
|
||||
console.log(selectedFile, selectedLayout, chatId);
|
||||
|
||||
return { selectedFile, selectedLayout, chatId };
|
||||
};
|
||||
|
|
|
@ -90,6 +90,7 @@ export const MOCK_CHAT: BusterChat = {
|
|||
response_messages: [
|
||||
createMockResponseMessageText(),
|
||||
createMockResponseMessageFile(),
|
||||
createMockResponseMessageFile(),
|
||||
createMockResponseMessageText(),
|
||||
createMockResponseMessageText()
|
||||
]
|
||||
|
|
|
@ -4,7 +4,13 @@ import { useMemoizedFn } from 'ahooks';
|
|||
import { BusterChat } from '@/api/asset_interfaces';
|
||||
import { IBusterChat } from '../interfaces';
|
||||
import { chatUpgrader } from './helpers';
|
||||
import { MOCK_CHAT } from './MOCK_CHAT';
|
||||
import {
|
||||
createMockResponseMessageFile,
|
||||
createMockResponseMessageText,
|
||||
createMockResponseMessageThought,
|
||||
MOCK_CHAT
|
||||
} from './MOCK_CHAT';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
export const useChatSubscriptions = ({
|
||||
chatsRef,
|
||||
|
@ -47,6 +53,73 @@ export const useChatSubscriptions = ({
|
|||
// });
|
||||
});
|
||||
|
||||
useHotkeys('t', () => {
|
||||
const newThoughts = createMockResponseMessageThought();
|
||||
const myChat = {
|
||||
...chatsRef.current[MOCK_CHAT.id]!,
|
||||
messages: [
|
||||
{
|
||||
...chatsRef.current[MOCK_CHAT.id]!.messages[0],
|
||||
reasoning: [...chatsRef.current[MOCK_CHAT.id]!.messages[0].reasoning, newThoughts],
|
||||
isCompletedStream: false
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chatsRef.current[MOCK_CHAT.id] = myChat;
|
||||
|
||||
startTransition(() => {
|
||||
// Create a new reference to trigger React update
|
||||
chatsRef.current = { ...chatsRef.current };
|
||||
});
|
||||
});
|
||||
|
||||
useHotkeys('m', () => {
|
||||
const newTextMessage = createMockResponseMessageText();
|
||||
const myChat = {
|
||||
...chatsRef.current[MOCK_CHAT.id]!,
|
||||
messages: [
|
||||
{
|
||||
...chatsRef.current[MOCK_CHAT.id]!.messages[0],
|
||||
response_messages: [
|
||||
...chatsRef.current[MOCK_CHAT.id]!.messages[0]!.response_messages,
|
||||
newTextMessage
|
||||
],
|
||||
isCompletedStream: false
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chatsRef.current[MOCK_CHAT.id] = myChat;
|
||||
|
||||
startTransition(() => {
|
||||
chatsRef.current = { ...chatsRef.current };
|
||||
});
|
||||
});
|
||||
|
||||
useHotkeys('f', () => {
|
||||
const newFileMessage = createMockResponseMessageFile();
|
||||
const myChat = {
|
||||
...chatsRef.current[MOCK_CHAT.id]!,
|
||||
messages: [
|
||||
{
|
||||
...chatsRef.current[MOCK_CHAT.id]!.messages[0],
|
||||
response_messages: [
|
||||
...chatsRef.current[MOCK_CHAT.id]!.messages[0]!.response_messages,
|
||||
newFileMessage
|
||||
],
|
||||
isCompletedStream: false
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chatsRef.current[MOCK_CHAT.id] = myChat;
|
||||
|
||||
startTransition(() => {
|
||||
chatsRef.current = { ...chatsRef.current };
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
unsubscribeFromChat,
|
||||
subscribeToChat
|
||||
|
|
|
@ -24,7 +24,7 @@ export enum BusterAppRoutes {
|
|||
|
||||
//NEW CHAT
|
||||
APP_CHAT_ID = '/app/chat/:chatId',
|
||||
APP_CHAT_ID_REASONING_ID = '/app/chat/:chatId/reasoning/:reasoningId',
|
||||
APP_CHAT_ID_REASONING_ID = '/app/chat/:chatId/reasoning/:messageId',
|
||||
APP_CHAT_ID_METRIC_ID = '/app/chat/:chatId/metric/:metricId',
|
||||
APP_CHAT_ID_COLLECTION_ID = '/app/chat/:chatId/collection/:collectionId',
|
||||
APP_CHAT_ID_DASHBOARD_ID = '/app/chat/:chatId/dashboard/:dashboardId',
|
||||
|
|
Loading…
Reference in New Issue