2024-04-09 10:51:05 +00:00
|
|
|
'use client';
|
|
|
|
|
2024-05-18 07:40:39 +00:00
|
|
|
import { useEffect, useRef, useState } from 'react';
|
2024-04-09 10:51:05 +00:00
|
|
|
import { Document } from '@langchain/core/documents';
|
|
|
|
import Navbar from './Navbar';
|
|
|
|
import Chat from './Chat';
|
|
|
|
import EmptyChat from './EmptyChat';
|
2024-06-29 05:39:51 +00:00
|
|
|
import crypto from 'crypto';
|
2024-05-04 09:26:54 +00:00
|
|
|
import { toast } from 'sonner';
|
2024-05-11 06:39:39 +00:00
|
|
|
import { useSearchParams } from 'next/navigation';
|
2024-05-18 07:40:39 +00:00
|
|
|
import { getSuggestions } from '@/lib/actions';
|
2024-06-29 06:41:34 +00:00
|
|
|
import Error from 'next/error';
|
2024-04-09 10:51:05 +00:00
|
|
|
|
|
|
|
export type Message = {
|
2024-06-29 05:39:51 +00:00
|
|
|
messageId: string;
|
|
|
|
chatId: string;
|
2024-04-09 13:40:15 +00:00
|
|
|
createdAt: Date;
|
2024-04-09 10:51:05 +00:00
|
|
|
content: string;
|
|
|
|
role: 'user' | 'assistant';
|
2024-05-18 07:40:39 +00:00
|
|
|
suggestions?: string[];
|
2024-04-09 10:51:05 +00:00
|
|
|
sources?: Document[];
|
|
|
|
};
|
|
|
|
|
2024-07-18 19:54:02 +00:00
|
|
|
const fetchSettings = async () => {
|
|
|
|
try {
|
|
|
|
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/settings`);
|
|
|
|
const settings = await res.json();
|
2024-07-30 10:34:03 +00:00
|
|
|
|
2024-07-18 19:54:02 +00:00
|
|
|
if (settings) {
|
|
|
|
localStorage.setItem('chatModelProvider', settings.chatModelProvider);
|
|
|
|
localStorage.setItem('chatModel', settings.chatModel);
|
2024-12-19 19:01:53 +00:00
|
|
|
localStorage.setItem(
|
|
|
|
'embeddingModelProvider',
|
|
|
|
settings.embeddingModelProvider,
|
|
|
|
);
|
2024-07-18 19:54:02 +00:00
|
|
|
localStorage.setItem('embeddingModel', settings.embeddingModel);
|
|
|
|
localStorage.setItem('openAIApiKey', settings.openAIApiKey);
|
|
|
|
localStorage.setItem('openAIBaseURL', settings.openAIBaseURL);
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
console.error('Failed to fetch settings:', err);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-11-23 09:34:19 +00:00
|
|
|
export interface File {
|
|
|
|
fileName: string;
|
|
|
|
fileExtension: string;
|
|
|
|
fileId: string;
|
|
|
|
}
|
|
|
|
|
2024-06-25 10:13:36 +00:00
|
|
|
const useSocket = (
|
|
|
|
url: string,
|
2024-06-29 05:39:51 +00:00
|
|
|
setIsWSReady: (ready: boolean) => void,
|
2024-06-25 10:13:36 +00:00
|
|
|
setError: (error: boolean) => void,
|
2024-12-19 19:01:53 +00:00
|
|
|
hasError: boolean,
|
2024-06-25 10:13:36 +00:00
|
|
|
) => {
|
2024-04-09 10:51:05 +00:00
|
|
|
const [ws, setWs] = useState<WebSocket | null>(null);
|
2024-08-01 15:12:21 +00:00
|
|
|
const reconnectTimeout = useRef(0);
|
|
|
|
const reconnectAttempts = useRef(0);
|
2024-04-09 10:51:05 +00:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (!ws) {
|
2024-05-02 06:44:26 +00:00
|
|
|
const connectWs = async () => {
|
2024-07-18 19:54:02 +00:00
|
|
|
await fetchSettings();
|
|
|
|
|
2024-05-02 06:44:26 +00:00
|
|
|
let chatModel = localStorage.getItem('chatModel');
|
|
|
|
let chatModelProvider = localStorage.getItem('chatModelProvider');
|
2024-05-04 05:21:06 +00:00
|
|
|
let embeddingModel = localStorage.getItem('embeddingModel');
|
|
|
|
let embeddingModelProvider = localStorage.getItem(
|
|
|
|
'embeddingModelProvider',
|
|
|
|
);
|
2024-05-02 06:44:26 +00:00
|
|
|
|
2024-08-04 12:44:46 +00:00
|
|
|
const providers = await fetch(
|
|
|
|
`${process.env.NEXT_PUBLIC_API_URL}/models`,
|
|
|
|
{
|
|
|
|
headers: {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
).then(async (res) => await res.json());
|
|
|
|
|
2024-05-04 05:21:06 +00:00
|
|
|
if (
|
|
|
|
!chatModel ||
|
|
|
|
!chatModelProvider ||
|
|
|
|
!embeddingModel ||
|
|
|
|
!embeddingModelProvider
|
|
|
|
) {
|
2024-08-04 12:44:46 +00:00
|
|
|
if (!chatModel || !chatModelProvider) {
|
|
|
|
const chatModelProviders = providers.chatModelProviders;
|
2024-05-04 05:21:06 +00:00
|
|
|
|
2024-08-04 12:44:46 +00:00
|
|
|
chatModelProvider = Object.keys(chatModelProviders)[0];
|
2024-05-02 06:44:26 +00:00
|
|
|
|
2024-08-04 12:44:46 +00:00
|
|
|
if (chatModelProvider === 'custom_openai') {
|
2024-09-25 11:52:21 +00:00
|
|
|
toast.error(
|
|
|
|
'Seems like you are using the custom OpenAI provider, please open the settings and configure the API key and base URL',
|
|
|
|
);
|
2024-08-04 12:44:46 +00:00
|
|
|
setError(true);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
chatModel = Object.keys(chatModelProviders[chatModelProvider])[0];
|
|
|
|
if (
|
|
|
|
!chatModelProviders ||
|
|
|
|
Object.keys(chatModelProviders).length === 0
|
|
|
|
)
|
|
|
|
return toast.error('No chat models available');
|
|
|
|
}
|
|
|
|
}
|
2024-07-25 15:06:26 +00:00
|
|
|
|
2024-08-04 12:44:46 +00:00
|
|
|
if (!embeddingModel || !embeddingModelProvider) {
|
|
|
|
const embeddingModelProviders = providers.embeddingModelProviders;
|
2024-07-25 15:06:26 +00:00
|
|
|
|
|
|
|
if (
|
2024-08-04 12:44:46 +00:00
|
|
|
!embeddingModelProviders ||
|
|
|
|
Object.keys(embeddingModelProviders).length === 0
|
2024-07-25 15:06:26 +00:00
|
|
|
)
|
2024-08-04 12:44:46 +00:00
|
|
|
return toast.error('No embedding models available');
|
2024-07-25 15:06:26 +00:00
|
|
|
|
2024-08-04 12:44:46 +00:00
|
|
|
embeddingModelProvider = Object.keys(embeddingModelProviders)[0];
|
|
|
|
embeddingModel = Object.keys(
|
|
|
|
embeddingModelProviders[embeddingModelProvider],
|
|
|
|
)[0];
|
|
|
|
}
|
2024-05-04 05:21:06 +00:00
|
|
|
|
2024-05-02 06:44:26 +00:00
|
|
|
localStorage.setItem('chatModel', chatModel!);
|
|
|
|
localStorage.setItem('chatModelProvider', chatModelProvider);
|
2024-05-04 05:21:06 +00:00
|
|
|
localStorage.setItem('embeddingModel', embeddingModel!);
|
|
|
|
localStorage.setItem(
|
|
|
|
'embeddingModelProvider',
|
|
|
|
embeddingModelProvider,
|
|
|
|
);
|
2024-07-09 10:51:45 +00:00
|
|
|
} else {
|
|
|
|
const chatModelProviders = providers.chatModelProviders;
|
|
|
|
const embeddingModelProviders = providers.embeddingModelProviders;
|
|
|
|
|
|
|
|
if (
|
|
|
|
Object.keys(chatModelProviders).length > 0 &&
|
|
|
|
!chatModelProviders[chatModelProvider]
|
|
|
|
) {
|
|
|
|
chatModelProvider = Object.keys(chatModelProviders)[0];
|
|
|
|
localStorage.setItem('chatModelProvider', chatModelProvider);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
chatModelProvider &&
|
2024-07-25 15:06:26 +00:00
|
|
|
chatModelProvider != 'custom_openai' &&
|
2024-07-09 10:51:45 +00:00
|
|
|
!chatModelProviders[chatModelProvider][chatModel]
|
|
|
|
) {
|
|
|
|
chatModel = Object.keys(chatModelProviders[chatModelProvider])[0];
|
|
|
|
localStorage.setItem('chatModel', chatModel);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
Object.keys(embeddingModelProviders).length > 0 &&
|
|
|
|
!embeddingModelProviders[embeddingModelProvider]
|
|
|
|
) {
|
|
|
|
embeddingModelProvider = Object.keys(embeddingModelProviders)[0];
|
|
|
|
localStorage.setItem(
|
|
|
|
'embeddingModelProvider',
|
|
|
|
embeddingModelProvider,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
embeddingModelProvider &&
|
|
|
|
!embeddingModelProviders[embeddingModelProvider][embeddingModel]
|
|
|
|
) {
|
|
|
|
embeddingModel = Object.keys(
|
|
|
|
embeddingModelProviders[embeddingModelProvider],
|
|
|
|
)[0];
|
|
|
|
localStorage.setItem('embeddingModel', embeddingModel);
|
|
|
|
}
|
2024-05-02 06:44:26 +00:00
|
|
|
}
|
|
|
|
|
2024-05-04 05:21:06 +00:00
|
|
|
const wsURL = new URL(url);
|
|
|
|
const searchParams = new URLSearchParams({});
|
|
|
|
|
|
|
|
searchParams.append('chatModel', chatModel!);
|
|
|
|
searchParams.append('chatModelProvider', chatModelProvider);
|
|
|
|
|
|
|
|
if (chatModelProvider === 'custom_openai') {
|
|
|
|
searchParams.append(
|
|
|
|
'openAIApiKey',
|
|
|
|
localStorage.getItem('openAIApiKey')!,
|
|
|
|
);
|
|
|
|
searchParams.append(
|
|
|
|
'openAIBaseURL',
|
|
|
|
localStorage.getItem('openAIBaseURL')!,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
searchParams.append('embeddingModel', embeddingModel!);
|
|
|
|
searchParams.append('embeddingModelProvider', embeddingModelProvider);
|
|
|
|
|
|
|
|
wsURL.search = searchParams.toString();
|
|
|
|
|
|
|
|
const ws = new WebSocket(wsURL.toString());
|
2024-05-04 09:26:54 +00:00
|
|
|
|
2024-06-25 10:13:36 +00:00
|
|
|
const timeoutId = setTimeout(() => {
|
|
|
|
if (ws.readyState !== 1) {
|
|
|
|
toast.error(
|
|
|
|
'Failed to connect to the server. Please try again later.',
|
|
|
|
);
|
|
|
|
}
|
2024-06-25 10:41:39 +00:00
|
|
|
}, 10000);
|
2024-06-25 10:13:36 +00:00
|
|
|
|
2024-10-15 12:34:50 +00:00
|
|
|
ws.addEventListener('message', (e) => {
|
|
|
|
const data = JSON.parse(e.data);
|
|
|
|
if (data.type === 'signal' && data.data === 'open') {
|
|
|
|
const interval = setInterval(() => {
|
|
|
|
if (ws.readyState === 1) {
|
2024-12-19 19:01:53 +00:00
|
|
|
reconnectTimeout.current = 0;
|
2024-10-15 12:34:50 +00:00
|
|
|
setIsWSReady(true);
|
|
|
|
clearInterval(interval);
|
|
|
|
}
|
|
|
|
}, 5);
|
|
|
|
clearTimeout(timeoutId);
|
2024-12-19 19:01:53 +00:00
|
|
|
reconnectTimeout.current = 0;
|
|
|
|
reconnectAttempts.current = 0;
|
2024-10-15 12:34:50 +00:00
|
|
|
console.log('[DEBUG] opened');
|
|
|
|
}
|
|
|
|
if (data.type === 'error') {
|
|
|
|
toast.error(data.data);
|
|
|
|
}
|
|
|
|
});
|
2024-05-04 09:26:54 +00:00
|
|
|
|
2024-06-25 10:13:36 +00:00
|
|
|
ws.onerror = () => {
|
|
|
|
clearTimeout(timeoutId);
|
|
|
|
setError(true);
|
|
|
|
toast.error('WebSocket connection error.');
|
|
|
|
};
|
2024-05-11 06:39:39 +00:00
|
|
|
|
2024-06-25 10:13:36 +00:00
|
|
|
ws.onclose = () => {
|
|
|
|
clearTimeout(timeoutId);
|
2024-08-01 15:12:21 +00:00
|
|
|
if (!hasError && reconnectAttempts.current < 3) {
|
2024-07-30 17:45:20 +00:00
|
|
|
setWs(null); // forces websocket to reopen when needed.
|
|
|
|
}
|
2024-06-25 10:13:36 +00:00
|
|
|
console.log('[DEBUG] closed');
|
2024-05-04 09:26:54 +00:00
|
|
|
};
|
2024-06-25 10:13:36 +00:00
|
|
|
|
|
|
|
setWs(ws);
|
2024-04-09 10:51:05 +00:00
|
|
|
};
|
2024-05-02 06:44:26 +00:00
|
|
|
|
2024-08-01 15:12:21 +00:00
|
|
|
if (reconnectAttempts.current < 3) {
|
2024-12-19 19:01:53 +00:00
|
|
|
console.log(
|
|
|
|
`[DEBUG] Attempting to reconnect (${reconnectAttempts.current + 1}/3)`,
|
|
|
|
);
|
|
|
|
setTimeout(connectWs, reconnectTimeout.current);
|
|
|
|
reconnectTimeout.current =
|
|
|
|
reconnectTimeout.current > 0 ? reconnectTimeout.current * 2 : 1000;
|
|
|
|
reconnectAttempts.current += 1;
|
2024-08-01 15:12:21 +00:00
|
|
|
} else {
|
2024-12-19 19:01:53 +00:00
|
|
|
console.log('[DEBUG] WebSocket reconnect failure after 3 retries');
|
|
|
|
setError(true);
|
2024-08-01 15:12:21 +00:00
|
|
|
}
|
2024-04-09 10:51:05 +00:00
|
|
|
}
|
2024-06-29 05:39:51 +00:00
|
|
|
}, [ws, url, setIsWSReady, setError]);
|
2024-04-09 10:51:05 +00:00
|
|
|
|
|
|
|
return ws;
|
|
|
|
};
|
|
|
|
|
2024-06-29 05:39:51 +00:00
|
|
|
const loadMessages = async (
|
|
|
|
chatId: string,
|
|
|
|
setMessages: (messages: Message[]) => void,
|
|
|
|
setIsMessagesLoaded: (loaded: boolean) => void,
|
|
|
|
setChatHistory: (history: [string, string][]) => void,
|
|
|
|
setFocusMode: (mode: string) => void,
|
2024-06-29 06:41:34 +00:00
|
|
|
setNotFound: (notFound: boolean) => void,
|
2024-11-23 09:34:19 +00:00
|
|
|
setFiles: (files: File[]) => void,
|
|
|
|
setFileIds: (fileIds: string[]) => void,
|
2024-06-29 05:39:51 +00:00
|
|
|
) => {
|
|
|
|
const res = await fetch(
|
|
|
|
`${process.env.NEXT_PUBLIC_API_URL}/chats/${chatId}`,
|
|
|
|
{
|
|
|
|
method: 'GET',
|
|
|
|
headers: {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2024-06-29 06:41:34 +00:00
|
|
|
if (res.status === 404) {
|
|
|
|
setNotFound(true);
|
|
|
|
setIsMessagesLoaded(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-06-29 05:39:51 +00:00
|
|
|
const data = await res.json();
|
|
|
|
|
|
|
|
const messages = data.messages.map((msg: any) => {
|
|
|
|
return {
|
|
|
|
...msg,
|
|
|
|
...JSON.parse(msg.metadata),
|
|
|
|
};
|
|
|
|
}) as Message[];
|
|
|
|
|
|
|
|
setMessages(messages);
|
|
|
|
|
|
|
|
const history = messages.map((msg) => {
|
|
|
|
return [msg.role, msg.content];
|
|
|
|
}) as [string, string][];
|
|
|
|
|
|
|
|
console.log('[DEBUG] messages loaded');
|
|
|
|
|
|
|
|
document.title = messages[0].content;
|
|
|
|
|
2024-11-23 09:34:19 +00:00
|
|
|
const files = data.chat.files.map((file: any) => {
|
|
|
|
return {
|
|
|
|
fileName: file.name,
|
|
|
|
fileExtension: file.name.split('.').pop(),
|
|
|
|
fileId: file.fileId,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
setFiles(files);
|
|
|
|
setFileIds(files.map((file: File) => file.fileId));
|
|
|
|
|
2024-06-29 05:39:51 +00:00
|
|
|
setChatHistory(history);
|
|
|
|
setFocusMode(data.chat.focusMode);
|
|
|
|
setIsMessagesLoaded(true);
|
|
|
|
};
|
|
|
|
|
|
|
|
const ChatWindow = ({ id }: { id?: string }) => {
|
2024-05-11 06:39:39 +00:00
|
|
|
const searchParams = useSearchParams();
|
|
|
|
const initialMessage = searchParams.get('q');
|
|
|
|
|
2024-06-29 05:39:51 +00:00
|
|
|
const [chatId, setChatId] = useState<string | undefined>(id);
|
|
|
|
const [newChatCreated, setNewChatCreated] = useState(false);
|
|
|
|
|
2024-06-25 10:13:36 +00:00
|
|
|
const [hasError, setHasError] = useState(false);
|
2024-06-29 05:39:51 +00:00
|
|
|
const [isReady, setIsReady] = useState(false);
|
|
|
|
|
|
|
|
const [isWSReady, setIsWSReady] = useState(false);
|
2024-06-25 10:13:36 +00:00
|
|
|
const ws = useSocket(
|
|
|
|
process.env.NEXT_PUBLIC_WS_URL!,
|
2024-06-29 05:39:51 +00:00
|
|
|
setIsWSReady,
|
2024-06-25 10:13:36 +00:00
|
|
|
setHasError,
|
2024-12-19 19:01:53 +00:00
|
|
|
hasError,
|
2024-06-25 10:13:36 +00:00
|
|
|
);
|
2024-05-11 06:39:39 +00:00
|
|
|
|
2024-04-09 10:51:05 +00:00
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const [messageAppeared, setMessageAppeared] = useState(false);
|
2024-06-29 05:39:51 +00:00
|
|
|
|
|
|
|
const [chatHistory, setChatHistory] = useState<[string, string][]>([]);
|
|
|
|
const [messages, setMessages] = useState<Message[]>([]);
|
|
|
|
|
2024-11-23 09:34:19 +00:00
|
|
|
const [files, setFiles] = useState<File[]>([]);
|
|
|
|
const [fileIds, setFileIds] = useState<string[]>([]);
|
|
|
|
|
2024-04-13 06:41:47 +00:00
|
|
|
const [focusMode, setFocusMode] = useState('webSearch');
|
2024-10-11 05:05:59 +00:00
|
|
|
const [optimizationMode, setOptimizationMode] = useState('speed');
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-06-29 05:39:51 +00:00
|
|
|
const [isMessagesLoaded, setIsMessagesLoaded] = useState(false);
|
|
|
|
|
2024-06-29 06:41:34 +00:00
|
|
|
const [notFound, setNotFound] = useState(false);
|
|
|
|
|
2024-06-29 05:39:51 +00:00
|
|
|
useEffect(() => {
|
|
|
|
if (
|
|
|
|
chatId &&
|
|
|
|
!newChatCreated &&
|
|
|
|
!isMessagesLoaded &&
|
|
|
|
messages.length === 0
|
|
|
|
) {
|
|
|
|
loadMessages(
|
|
|
|
chatId,
|
|
|
|
setMessages,
|
|
|
|
setIsMessagesLoaded,
|
|
|
|
setChatHistory,
|
|
|
|
setFocusMode,
|
2024-06-29 06:41:34 +00:00
|
|
|
setNotFound,
|
2024-11-23 09:34:19 +00:00
|
|
|
setFiles,
|
|
|
|
setFileIds,
|
2024-06-29 05:39:51 +00:00
|
|
|
);
|
|
|
|
} else if (!chatId) {
|
|
|
|
setNewChatCreated(true);
|
|
|
|
setIsMessagesLoaded(true);
|
|
|
|
setChatId(crypto.randomBytes(20).toString('hex'));
|
|
|
|
}
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, []);
|
|
|
|
|
2024-09-25 11:52:21 +00:00
|
|
|
useEffect(() => {
|
|
|
|
return () => {
|
|
|
|
if (ws?.readyState === 1) {
|
|
|
|
ws.close();
|
|
|
|
console.log('[DEBUG] closed');
|
|
|
|
}
|
|
|
|
};
|
2024-10-30 03:44:09 +00:00
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2024-09-25 11:52:21 +00:00
|
|
|
}, []);
|
|
|
|
|
2024-06-29 05:39:51 +00:00
|
|
|
const messagesRef = useRef<Message[]>([]);
|
|
|
|
|
2024-07-18 19:54:02 +00:00
|
|
|
useEffect(() => {
|
|
|
|
fetchSettings();
|
|
|
|
}, []);
|
2024-05-18 07:40:39 +00:00
|
|
|
useEffect(() => {
|
|
|
|
messagesRef.current = messages;
|
|
|
|
}, [messages]);
|
|
|
|
|
2024-06-29 05:39:51 +00:00
|
|
|
useEffect(() => {
|
|
|
|
if (isMessagesLoaded && isWSReady) {
|
|
|
|
setIsReady(true);
|
2024-10-15 12:34:50 +00:00
|
|
|
console.log('[DEBUG] ready');
|
2024-06-29 05:39:51 +00:00
|
|
|
}
|
|
|
|
}, [isMessagesLoaded, isWSReady]);
|
|
|
|
|
2024-10-17 13:21:11 +00:00
|
|
|
const sendMessage = async (message: string, messageId?: string) => {
|
2024-04-09 10:51:05 +00:00
|
|
|
if (loading) return;
|
2024-10-17 13:21:11 +00:00
|
|
|
|
2024-04-09 10:51:05 +00:00
|
|
|
setLoading(true);
|
|
|
|
setMessageAppeared(false);
|
|
|
|
|
|
|
|
let sources: Document[] | undefined = undefined;
|
|
|
|
let recievedMessage = '';
|
|
|
|
let added = false;
|
|
|
|
|
2024-10-17 13:21:11 +00:00
|
|
|
messageId = messageId ?? crypto.randomBytes(7).toString('hex');
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-04-09 10:51:05 +00:00
|
|
|
ws?.send(
|
|
|
|
JSON.stringify({
|
|
|
|
type: 'message',
|
2024-06-29 05:39:51 +00:00
|
|
|
message: {
|
2024-10-17 13:21:11 +00:00
|
|
|
messageId: messageId,
|
2024-06-29 05:39:51 +00:00
|
|
|
chatId: chatId!,
|
|
|
|
content: message,
|
|
|
|
},
|
2024-11-23 09:34:19 +00:00
|
|
|
files: fileIds,
|
2024-04-13 06:41:47 +00:00
|
|
|
focusMode: focusMode,
|
2024-10-11 05:05:59 +00:00
|
|
|
optimizationMode: optimizationMode,
|
2024-04-09 10:51:05 +00:00
|
|
|
history: [...chatHistory, ['human', message]],
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
setMessages((prevMessages) => [
|
|
|
|
...prevMessages,
|
|
|
|
{
|
|
|
|
content: message,
|
2024-06-29 05:39:51 +00:00
|
|
|
messageId: messageId,
|
|
|
|
chatId: chatId!,
|
2024-04-09 10:51:05 +00:00
|
|
|
role: 'user',
|
2024-04-09 13:40:15 +00:00
|
|
|
createdAt: new Date(),
|
2024-04-09 10:51:05 +00:00
|
|
|
},
|
|
|
|
]);
|
|
|
|
|
2024-05-18 07:40:39 +00:00
|
|
|
const messageHandler = async (e: MessageEvent) => {
|
2024-04-09 10:51:05 +00:00
|
|
|
const data = JSON.parse(e.data);
|
|
|
|
|
2024-05-04 09:26:54 +00:00
|
|
|
if (data.type === 'error') {
|
|
|
|
toast.error(data.data);
|
|
|
|
setLoading(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-04-09 10:51:05 +00:00
|
|
|
if (data.type === 'sources') {
|
|
|
|
sources = data.data;
|
|
|
|
if (!added) {
|
|
|
|
setMessages((prevMessages) => [
|
|
|
|
...prevMessages,
|
|
|
|
{
|
|
|
|
content: '',
|
2024-06-29 05:39:51 +00:00
|
|
|
messageId: data.messageId,
|
|
|
|
chatId: chatId!,
|
2024-04-09 10:51:05 +00:00
|
|
|
role: 'assistant',
|
|
|
|
sources: sources,
|
2024-04-09 13:40:15 +00:00
|
|
|
createdAt: new Date(),
|
2024-04-09 10:51:05 +00:00
|
|
|
},
|
|
|
|
]);
|
|
|
|
added = true;
|
|
|
|
}
|
|
|
|
setMessageAppeared(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data.type === 'message') {
|
|
|
|
if (!added) {
|
|
|
|
setMessages((prevMessages) => [
|
|
|
|
...prevMessages,
|
|
|
|
{
|
|
|
|
content: data.data,
|
2024-06-29 05:39:51 +00:00
|
|
|
messageId: data.messageId,
|
|
|
|
chatId: chatId!,
|
2024-04-09 10:51:05 +00:00
|
|
|
role: 'assistant',
|
|
|
|
sources: sources,
|
2024-04-09 13:40:15 +00:00
|
|
|
createdAt: new Date(),
|
2024-04-09 10:51:05 +00:00
|
|
|
},
|
|
|
|
]);
|
|
|
|
added = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
setMessages((prev) =>
|
|
|
|
prev.map((message) => {
|
2024-06-29 05:39:51 +00:00
|
|
|
if (message.messageId === data.messageId) {
|
2024-04-09 10:51:05 +00:00
|
|
|
return { ...message, content: message.content + data.data };
|
|
|
|
}
|
|
|
|
|
|
|
|
return message;
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
recievedMessage += data.data;
|
|
|
|
setMessageAppeared(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data.type === 'messageEnd') {
|
|
|
|
setChatHistory((prevHistory) => [
|
|
|
|
...prevHistory,
|
|
|
|
['human', message],
|
|
|
|
['assistant', recievedMessage],
|
|
|
|
]);
|
2024-05-18 07:40:39 +00:00
|
|
|
|
2024-04-09 10:51:05 +00:00
|
|
|
ws?.removeEventListener('message', messageHandler);
|
|
|
|
setLoading(false);
|
2024-05-18 07:40:39 +00:00
|
|
|
|
|
|
|
const lastMsg = messagesRef.current[messagesRef.current.length - 1];
|
|
|
|
|
|
|
|
if (
|
|
|
|
lastMsg.role === 'assistant' &&
|
|
|
|
lastMsg.sources &&
|
|
|
|
lastMsg.sources.length > 0 &&
|
|
|
|
!lastMsg.suggestions
|
|
|
|
) {
|
|
|
|
const suggestions = await getSuggestions(messagesRef.current);
|
|
|
|
setMessages((prev) =>
|
|
|
|
prev.map((msg) => {
|
2024-06-29 05:39:51 +00:00
|
|
|
if (msg.messageId === lastMsg.messageId) {
|
2024-05-18 07:40:39 +00:00
|
|
|
return { ...msg, suggestions: suggestions };
|
|
|
|
}
|
|
|
|
return msg;
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
2024-04-09 10:51:05 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ws?.addEventListener('message', messageHandler);
|
|
|
|
};
|
|
|
|
|
|
|
|
const rewrite = (messageId: string) => {
|
2024-06-29 05:39:51 +00:00
|
|
|
const index = messages.findIndex((msg) => msg.messageId === messageId);
|
2024-04-09 10:51:05 +00:00
|
|
|
|
|
|
|
if (index === -1) return;
|
|
|
|
|
|
|
|
const message = messages[index - 1];
|
|
|
|
|
|
|
|
setMessages((prev) => {
|
|
|
|
return [...prev.slice(0, messages.length > 2 ? index - 1 : 0)];
|
|
|
|
});
|
|
|
|
setChatHistory((prev) => {
|
|
|
|
return [...prev.slice(0, messages.length > 2 ? index - 1 : 0)];
|
|
|
|
});
|
|
|
|
|
2024-10-17 13:21:11 +00:00
|
|
|
sendMessage(message.content, message.messageId);
|
2024-04-09 10:51:05 +00:00
|
|
|
};
|
|
|
|
|
2024-05-11 06:39:39 +00:00
|
|
|
useEffect(() => {
|
2024-10-15 10:51:00 +00:00
|
|
|
if (isReady && initialMessage && ws?.readyState === 1) {
|
2024-05-11 06:39:39 +00:00
|
|
|
sendMessage(initialMessage);
|
|
|
|
}
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2024-10-15 12:34:50 +00:00
|
|
|
}, [ws?.readyState, isReady, initialMessage, isWSReady]);
|
2024-05-11 06:39:39 +00:00
|
|
|
|
2024-06-25 10:13:36 +00:00
|
|
|
if (hasError) {
|
|
|
|
return (
|
|
|
|
<div className="flex flex-col items-center justify-center min-h-screen">
|
2024-06-25 10:41:39 +00:00
|
|
|
<p className="dark:text-white/70 text-black/70 text-sm">
|
|
|
|
Failed to connect to the server. Please try again later.
|
|
|
|
</p>
|
2024-06-25 10:13:36 +00:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-05-11 06:39:39 +00:00
|
|
|
return isReady ? (
|
2024-06-29 06:41:34 +00:00
|
|
|
notFound ? (
|
|
|
|
<Error statusCode={404} />
|
|
|
|
) : (
|
|
|
|
<div>
|
|
|
|
{messages.length > 0 ? (
|
|
|
|
<>
|
2024-10-29 14:29:58 +00:00
|
|
|
<Navbar chatId={chatId!} messages={messages} />
|
2024-06-29 06:41:34 +00:00
|
|
|
<Chat
|
|
|
|
loading={loading}
|
|
|
|
messages={messages}
|
|
|
|
sendMessage={sendMessage}
|
|
|
|
messageAppeared={messageAppeared}
|
|
|
|
rewrite={rewrite}
|
2024-11-23 09:34:19 +00:00
|
|
|
fileIds={fileIds}
|
|
|
|
setFileIds={setFileIds}
|
|
|
|
files={files}
|
|
|
|
setFiles={setFiles}
|
2024-06-29 06:41:34 +00:00
|
|
|
/>
|
|
|
|
</>
|
|
|
|
) : (
|
|
|
|
<EmptyChat
|
2024-04-09 10:51:05 +00:00
|
|
|
sendMessage={sendMessage}
|
2024-06-29 06:41:34 +00:00
|
|
|
focusMode={focusMode}
|
|
|
|
setFocusMode={setFocusMode}
|
2024-10-11 05:05:59 +00:00
|
|
|
optimizationMode={optimizationMode}
|
|
|
|
setOptimizationMode={setOptimizationMode}
|
2024-11-23 09:34:19 +00:00
|
|
|
fileIds={fileIds}
|
|
|
|
setFileIds={setFileIds}
|
|
|
|
files={files}
|
|
|
|
setFiles={setFiles}
|
2024-04-09 10:51:05 +00:00
|
|
|
/>
|
2024-06-29 06:41:34 +00:00
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)
|
2024-04-20 04:53:56 +00:00
|
|
|
) : (
|
2024-04-20 05:48:52 +00:00
|
|
|
<div className="flex flex-row items-center justify-center min-h-screen">
|
|
|
|
<svg
|
|
|
|
aria-hidden="true"
|
2024-05-31 05:38:32 +00:00
|
|
|
className="w-8 h-8 text-light-200 fill-light-secondary dark:text-[#202020] animate-spin dark:fill-[#ffffff3b]"
|
2024-04-20 05:48:52 +00:00
|
|
|
viewBox="0 0 100 101"
|
|
|
|
fill="none"
|
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
|
|
>
|
|
|
|
<path
|
2024-06-25 10:13:36 +00:00
|
|
|
d="M100 50.5908C100.003 78.2051 78.1951 100.003 50.5908 100C22.9765 99.9972 0.997224 78.018 1 50.4037C1.00281 22.7993 22.8108 0.997224 50.4251 1C78.0395 1.00281 100.018 22.8108 100 50.4251ZM9.08164 50.594C9.06312 73.3997 27.7909 92.1272 50.5966 92.1457C73.4023 92.1642 92.1298 73.4365 92.1483 50.6308C92.1669 27.8251 73.4392 9.0973 50.6335 9.07878C27.8278 9.06026 9.10003 27.787 9.08164 50.594Z"
|
2024-04-20 05:48:52 +00:00
|
|
|
fill="currentColor"
|
|
|
|
/>
|
|
|
|
<path
|
2024-06-25 10:13:36 +00:00
|
|
|
d="M93.9676 39.0409C96.393 38.4037 97.8624 35.9116 96.9801 33.5533C95.1945 28.8227 92.871 24.3692 90.0681 20.348C85.6237 14.1775 79.4473 9.36872 72.0454 6.45794C64.6435 3.54717 56.3134 2.65431 48.3133 3.89319C45.869 4.27179 44.3768 6.77534 45.014 9.20079C45.6512 11.6262 48.1343 13.0956 50.5786 12.717C56.5073 11.8281 62.5542 12.5399 68.0406 14.7911C73.527 17.0422 78.2187 20.7487 81.5841 25.4923C83.7976 28.5886 85.4467 32.059 86.4416 35.7474C87.1273 38.1189 89.5423 39.6781 91.9676 39.0409Z"
|
2024-04-20 05:48:52 +00:00
|
|
|
fill="currentFill"
|
|
|
|
/>
|
2024-04-20 04:53:56 +00:00
|
|
|
</svg>
|
|
|
|
</div>
|
2024-04-20 05:48:52 +00:00
|
|
|
);
|
2024-04-09 10:51:05 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export default ChatWindow;
|