diff --git a/web/containers/ErrorMessage/index.test.tsx b/web/containers/ErrorMessage/index.test.tsx index 306a80e324..d9866d3c0f 100644 --- a/web/containers/ErrorMessage/index.test.tsx +++ b/web/containers/ErrorMessage/index.test.tsx @@ -30,20 +30,23 @@ describe('ErrorMessage Component', () => { beforeEach(() => { jest.clearAllMocks() - ;(useAtomValue as jest.Mock).mockReturnValue([]) - ;(useSetAtom as jest.Mock).mockReturnValue(mockSetMainState) - ;(useSetAtom as jest.Mock).mockReturnValue(mockSetSelectedSettingScreen) - ;(useSetAtom as jest.Mock).mockReturnValue(mockSetModalTroubleShooting) - ;(useSendChatMessage as jest.Mock).mockReturnValue({ - resendChatMessage: mockResendChatMessage, - }) + ; (useAtomValue as jest.Mock).mockReturnValue([]) + ; (useSetAtom as jest.Mock).mockReturnValue(mockSetMainState) + ; (useSetAtom as jest.Mock).mockReturnValue(mockSetSelectedSettingScreen) + ; (useSetAtom as jest.Mock).mockReturnValue(mockSetModalTroubleShooting) + ; (useSendChatMessage as jest.Mock).mockReturnValue({ + resendChatMessage: mockResendChatMessage, + }) }) it('renders error message with InvalidApiKey correctly', () => { const message: ThreadMessage = { id: '1', - status: MessageStatus.Error, - error_code: ErrorCode.InvalidApiKey, + metadata: { + error: MessageStatus.Error, + error_code: ErrorCode.InvalidApiKey, + }, + status: "completed", content: [{ text: { value: 'Invalid API Key' } }], } as ThreadMessage @@ -56,8 +59,11 @@ describe('ErrorMessage Component', () => { it('renders general error message correctly', () => { const message: ThreadMessage = { id: '1', - status: MessageStatus.Error, - error_code: ErrorCode.Unknown, + status: "completed", + metadata: { + error: MessageStatus.Error, + error_code: ErrorCode.Unknown + }, content: [{ text: { value: 'Unknown error occurred' } }], } as ThreadMessage @@ -69,9 +75,11 @@ describe('ErrorMessage Component', () => { it('opens troubleshooting modal when link is clicked', () => { const message: ThreadMessage = { id: '1', - status: MessageStatus.Error, - error_code: ErrorCode.Unknown, - content: [{ text: { value: 'Unknown error occurred' } }], + status: "completed", + metadata: { + error: MessageStatus.Error, + error_code: ErrorCode.Unknown, + }, content: [{ text: { value: 'Unknown error occurred' } }], } as ThreadMessage render() diff --git a/web/containers/ErrorMessage/index.tsx b/web/containers/ErrorMessage/index.tsx index 95b87fc53b..e0705e6b6d 100644 --- a/web/containers/ErrorMessage/index.tsx +++ b/web/containers/ErrorMessage/index.tsx @@ -53,7 +53,7 @@ const ErrorMessage = ({ message }: { message: ThreadMessage }) => { const getErrorTitle = () => { const engine = getEngine() - switch (message.error_code) { + switch (message.metadata?.error_code) { case ErrorCode.InvalidApiKey: case ErrorCode.AuthenticationError: return ( @@ -102,7 +102,7 @@ const ErrorMessage = ({ message }: { message: ThreadMessage }) => { return (
- {message.status === MessageStatus.Error && ( + {!!message.metadata?.error && (
{ const updatedMessage = await extensionManager .get(ExtensionTypeEnum.Conversational) ?.createMessage(message) + .catch(() => undefined) if (updatedMessage) { deleteMessage(message.id) addNewMessage(updatedMessage) diff --git a/web/helpers/atoms/ChatMessage.atom.ts b/web/helpers/atoms/ChatMessage.atom.ts index 7cdeb69463..5df44e0312 100644 --- a/web/helpers/atoms/ChatMessage.atom.ts +++ b/web/helpers/atoms/ChatMessage.atom.ts @@ -141,7 +141,7 @@ export const deleteMessageAtom = atom(null, (get, set, id: string) => { if (threadId) { // Should also delete error messages to clear out the error state newData[threadId] = newData[threadId].filter( - (e) => e.id !== id && e.status !== MessageStatus.Error + (e) => e.id !== id && !e.metadata?.error ) set(chatMessages, newData) diff --git a/web/hooks/useActiveModel.ts b/web/hooks/useActiveModel.ts index e436d116e0..ed704dd612 100644 --- a/web/hooks/useActiveModel.ts +++ b/web/hooks/useActiveModel.ts @@ -10,7 +10,6 @@ import { LAST_USED_MODEL_ID } from './useRecommendedModel' import { vulkanEnabledAtom } from '@/helpers/atoms/AppConfig.atom' import { activeAssistantAtom } from '@/helpers/atoms/Assistant.atom' import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom' -import { activeThreadAtom } from '@/helpers/atoms/Thread.atom' export const activeModelAtom = atom(undefined) export const loadModelErrorAtom = atom(undefined) @@ -29,7 +28,6 @@ export const stateModelAtom = atom({ export function useActiveModel() { const [activeModel, setActiveModel] = useAtom(activeModelAtom) - const activeThread = useAtomValue(activeThreadAtom) const [stateModel, setStateModel] = useAtom(stateModelAtom) const downloadedModels = useAtomValue(downloadedModelsAtom) const setLoadModelError = useSetAtom(loadModelErrorAtom) diff --git a/web/hooks/useCreateNewThread.ts b/web/hooks/useCreateNewThread.ts index 944b446545..c4c77d0a4f 100644 --- a/web/hooks/useCreateNewThread.ts +++ b/web/hooks/useCreateNewThread.ts @@ -230,8 +230,10 @@ export const useCreateNewThread = () => { await extensionManager .get(ExtensionTypeEnum.Conversational) ?.createThreadAssistant(thread.id, assistantInfo) + .catch(console.error) return thread }) + .catch(() => undefined) } return { diff --git a/web/hooks/usePath.ts b/web/hooks/usePath.ts index 315072000a..464ff0b580 100644 --- a/web/hooks/usePath.ts +++ b/web/hooks/usePath.ts @@ -1,4 +1,4 @@ -import { openFileExplorer, joinPath, baseName, fs } from '@janhq/core' +import { openFileExplorer, joinPath, baseName } from '@janhq/core' import { useAtomValue } from 'jotai' import { getFileInfo } from '@/utils/file' diff --git a/web/hooks/useSendChatMessage.ts b/web/hooks/useSendChatMessage.ts index f1582d2797..66b031849d 100644 --- a/web/hooks/useSendChatMessage.ts +++ b/web/hooks/useSendChatMessage.ts @@ -200,6 +200,7 @@ export default function useSendChatMessage() { const createdMessage = await extensionManager .get(ExtensionTypeEnum.Conversational) ?.createMessage(newMessage) + .catch(() => undefined) if (!createdMessage) return diff --git a/web/screens/Thread/ThreadCenterPanel/ChatItem/index.tsx b/web/screens/Thread/ThreadCenterPanel/ChatItem/index.tsx index 1fa3ef1155..57876d0448 100644 --- a/web/screens/Thread/ThreadCenterPanel/ChatItem/index.tsx +++ b/web/screens/Thread/ThreadCenterPanel/ChatItem/index.tsx @@ -23,9 +23,7 @@ const ChatItem = forwardRef((message, ref) => { const [content, setContent] = useState(message.content) const [status, setStatus] = useState(message.status) const [errorMessage, setErrorMessage] = useState( - message.isCurrentMessage && message.status === MessageStatus.Error - ? message - : undefined + message.isCurrentMessage && !!message?.metadata?.error ? message : undefined ) function onMessageUpdate(data: ThreadMessage) { @@ -52,16 +50,18 @@ const ChatItem = forwardRef((message, ref) => { return ( <> - {status !== MessageStatus.Error && content?.length > 0 && ( -
- -
- )} + {status !== MessageStatus.Error && + !message.metadata?.error && + content?.length > 0 && ( +
+ +
+ )} {errorMessage && !message.loadModelError && ( )} diff --git a/web/screens/Thread/ThreadCenterPanel/EditChatInput/index.tsx b/web/screens/Thread/ThreadCenterPanel/EditChatInput/index.tsx index 9b81ea6518..88f6a72b05 100644 --- a/web/screens/Thread/ThreadCenterPanel/EditChatInput/index.tsx +++ b/web/screens/Thread/ThreadCenterPanel/EditChatInput/index.tsx @@ -89,7 +89,7 @@ const EditChatInput: React.FC = ({ message }) => { .get(ExtensionTypeEnum.Conversational) ?.deleteMessage(message.thread_id, message.id) ) - ) + ).catch(console.error) setMessages(threadId, newMessages) sendChatMessage(editPrompt, false, newMessages) } diff --git a/web/screens/Thread/ThreadCenterPanel/MessageToolbar/index.tsx b/web/screens/Thread/ThreadCenterPanel/MessageToolbar/index.tsx index a15f0ec583..a7b59216a4 100644 --- a/web/screens/Thread/ThreadCenterPanel/MessageToolbar/index.tsx +++ b/web/screens/Thread/ThreadCenterPanel/MessageToolbar/index.tsx @@ -109,7 +109,7 @@ const MessageToolbar = ({ message }: { message: ThreadMessage }) => { )} {message.id === messages[messages.length - 1]?.id && - messages[messages.length - 1].status !== MessageStatus.Error && + !messages[messages.length - 1]?.metadata?.error && !messages[messages.length - 1].attachments?.length && (
{ +const DocMessage = ({ + id, + metadata, +}: { + id: string + metadata: Record | undefined +}) => { const { onViewFile } = usePath() - const [fileInfo, setFileInfo] = useState< - { filename: string; id: string } | undefined - >() - useEffect(() => { - if (!fileInfo) { - getFileInfo(id).then((data) => { - setFileInfo(data) - }) - } - }, [fileInfo, id]) return (
@@ -29,10 +25,14 @@ const DocMessage = ({ id }: { id: string }) => {
- {fileInfo?.filename} + {metadata && 'filename' in metadata + ? (metadata.filename as string) + : id}

- {fileInfo?.id ?? id} + {metadata && 'size' in metadata + ? toGibibytes(Number(metadata.size)) + : id}

diff --git a/web/screens/Thread/ThreadCenterPanel/TextMessage/index.tsx b/web/screens/Thread/ThreadCenterPanel/TextMessage/index.tsx index c029da74d7..e870e09e71 100644 --- a/web/screens/Thread/ThreadCenterPanel/TextMessage/index.tsx +++ b/web/screens/Thread/ThreadCenterPanel/TextMessage/index.tsx @@ -127,7 +127,10 @@ const MessageContainer: React.FC< <> {image && } {attachedFile && ( - + )} {editMessage === props.id ? ( diff --git a/web/utils/messageRequestBuilder.ts b/web/utils/messageRequestBuilder.ts index 269dfcee4c..c3da9cbd80 100644 --- a/web/utils/messageRequestBuilder.ts +++ b/web/utils/messageRequestBuilder.ts @@ -6,7 +6,6 @@ import { ChatCompletionRole, MessageRequest, MessageRequestType, - MessageStatus, ModelInfo, Thread, ThreadMessage, @@ -35,7 +34,7 @@ export class MessageRequestBuilder { this.model = model this.thread = thread this.messages = messages - .filter((e) => e.status !== MessageStatus.Error) + .filter((e) => !e.metadata?.error) .map((msg) => ({ role: msg.role, content: msg.content[0]?.text?.value ?? '.', diff --git a/web/utils/threadMessageBuilder.ts b/web/utils/threadMessageBuilder.ts index 8a776d5df3..1162dd2f62 100644 --- a/web/utils/threadMessageBuilder.ts +++ b/web/utils/threadMessageBuilder.ts @@ -16,6 +16,7 @@ export class ThreadMessageBuilder { content: ThreadContent[] = [] attachments: Attachment[] = [] + metadata: Record = {} constructor(messageRequest: MessageRequestBuilder) { this.messageRequest = messageRequest @@ -33,6 +34,7 @@ export class ThreadMessageBuilder { completed_at: timestamp, object: 'thread.message', content: this.content, + metadata: this.metadata, } } @@ -68,6 +70,10 @@ export class ThreadMessageBuilder { }, ], }) + this.metadata = { + filename: fileUpload.name, + size: fileUpload.file?.size, + } } return this