2024-08-29 16:51:59 +05:30
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import { useEffect, useRef, useState } from 'react';
|
|
|
|
|
import { Document } from '@langchain/core/documents';
|
|
|
|
|
import Navbar from './Navbar';
|
|
|
|
|
import Chat from './Chat';
|
|
|
|
|
import EmptyChat from './EmptyChat';
|
|
|
|
|
import crypto from 'crypto';
|
|
|
|
|
import { toast } from 'sonner';
|
|
|
|
|
import { useSearchParams } from 'next/navigation';
|
|
|
|
|
import { getSuggestions } from '@/lib/actions';
|
2025-01-11 13:10:10 +05:30
|
|
|
import { Settings } from 'lucide-react';
|
2025-02-15 11:31:08 +05:30
|
|
|
import Link from 'next/link';
|
2025-01-05 17:29:53 +00:00
|
|
|
import NextError from 'next/error';
|
2024-04-09 16:21:05 +05:30
|
|
|
|
|
|
|
|
export type Message = {
|
2024-08-29 16:51:59 +05:30
|
|
|
messageId: string;
|
|
|
|
|
chatId: string;
|
|
|
|
|
createdAt: Date;
|
|
|
|
|
content: string;
|
|
|
|
|
role: 'user' | 'assistant';
|
|
|
|
|
suggestions?: string[];
|
|
|
|
|
sources?: Document[];
|
|
|
|
|
};
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2024-11-23 15:04:19 +05:30
|
|
|
export interface File {
|
|
|
|
|
fileName: string;
|
|
|
|
|
fileExtension: string;
|
|
|
|
|
fileId: string;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-19 13:41:52 +05:30
|
|
|
interface ChatModelProvider {
|
|
|
|
|
name: string;
|
|
|
|
|
provider: string;
|
|
|
|
|
}
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2025-03-19 13:41:52 +05:30
|
|
|
interface EmbeddingModelProvider {
|
|
|
|
|
name: string;
|
|
|
|
|
provider: string;
|
|
|
|
|
}
|
2025-01-05 17:29:53 +00:00
|
|
|
|
2025-03-19 13:41:52 +05:30
|
|
|
const checkConfig = async (
|
|
|
|
|
setChatModelProvider: (provider: ChatModelProvider) => void,
|
|
|
|
|
setEmbeddingModelProvider: (provider: EmbeddingModelProvider) => void,
|
|
|
|
|
setIsConfigReady: (ready: boolean) => void,
|
|
|
|
|
setHasError: (hasError: boolean) => void,
|
|
|
|
|
) => {
|
2025-03-19 16:23:27 +05:30
|
|
|
try {
|
|
|
|
|
let chatModel = localStorage.getItem('chatModel');
|
|
|
|
|
let chatModelProvider = localStorage.getItem('chatModelProvider');
|
|
|
|
|
let embeddingModel = localStorage.getItem('embeddingModel');
|
|
|
|
|
let embeddingModelProvider = localStorage.getItem('embeddingModelProvider');
|
|
|
|
|
|
|
|
|
|
const autoImageSearch = localStorage.getItem('autoImageSearch');
|
|
|
|
|
const autoVideoSearch = localStorage.getItem('autoVideoSearch');
|
|
|
|
|
|
|
|
|
|
if (!autoImageSearch) {
|
|
|
|
|
localStorage.setItem('autoImageSearch', 'true');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!autoVideoSearch) {
|
|
|
|
|
localStorage.setItem('autoVideoSearch', 'false');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const providers = await fetch(`/api/models`, {
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
},
|
|
|
|
|
}).then(async (res) => {
|
|
|
|
|
if (!res.ok)
|
|
|
|
|
throw new Error(
|
|
|
|
|
`Failed to fetch models: ${res.status} ${res.statusText}`,
|
2024-08-29 16:51:59 +05:30
|
|
|
);
|
2025-03-19 16:23:27 +05:30
|
|
|
return res.json();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!chatModel ||
|
|
|
|
|
!chatModelProvider ||
|
|
|
|
|
!embeddingModel ||
|
|
|
|
|
!embeddingModelProvider
|
|
|
|
|
) {
|
|
|
|
|
if (!chatModel || !chatModelProvider) {
|
|
|
|
|
const chatModelProviders = providers.chatModelProviders;
|
|
|
|
|
|
|
|
|
|
chatModelProvider =
|
|
|
|
|
chatModelProvider || Object.keys(chatModelProviders)[0];
|
|
|
|
|
|
|
|
|
|
chatModel = Object.keys(chatModelProviders[chatModelProvider])[0];
|
|
|
|
|
|
|
|
|
|
if (!chatModelProviders || Object.keys(chatModelProviders).length === 0)
|
|
|
|
|
return toast.error('No chat models available');
|
|
|
|
|
}
|
2025-02-15 11:29:59 +05:30
|
|
|
|
2025-03-19 16:23:27 +05:30
|
|
|
if (!embeddingModel || !embeddingModelProvider) {
|
|
|
|
|
const embeddingModelProviders = providers.embeddingModelProviders;
|
2024-08-04 18:14:46 +05:30
|
|
|
|
2024-05-04 10:51:06 +05:30
|
|
|
if (
|
2025-03-19 16:23:27 +05:30
|
|
|
!embeddingModelProviders ||
|
|
|
|
|
Object.keys(embeddingModelProviders).length === 0
|
|
|
|
|
)
|
|
|
|
|
return toast.error('No embedding models available');
|
|
|
|
|
|
|
|
|
|
embeddingModelProvider = Object.keys(embeddingModelProviders)[0];
|
|
|
|
|
embeddingModel = Object.keys(
|
|
|
|
|
embeddingModelProviders[embeddingModelProvider],
|
|
|
|
|
)[0];
|
2025-01-05 17:29:53 +00:00
|
|
|
}
|
|
|
|
|
|
2025-03-19 16:23:27 +05:30
|
|
|
localStorage.setItem('chatModel', chatModel!);
|
|
|
|
|
localStorage.setItem('chatModelProvider', chatModelProvider);
|
|
|
|
|
localStorage.setItem('embeddingModel', embeddingModel!);
|
|
|
|
|
localStorage.setItem('embeddingModelProvider', embeddingModelProvider);
|
|
|
|
|
} else {
|
|
|
|
|
const chatModelProviders = providers.chatModelProviders;
|
|
|
|
|
const embeddingModelProviders = providers.embeddingModelProviders;
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
Object.keys(chatModelProviders).length > 0 &&
|
|
|
|
|
!chatModelProviders[chatModelProvider]
|
|
|
|
|
) {
|
|
|
|
|
const chatModelProvidersKeys = Object.keys(chatModelProviders);
|
|
|
|
|
chatModelProvider =
|
|
|
|
|
chatModelProvidersKeys.find(
|
|
|
|
|
(key) => Object.keys(chatModelProviders[key]).length > 0,
|
|
|
|
|
) || chatModelProvidersKeys[0];
|
|
|
|
|
|
|
|
|
|
localStorage.setItem('chatModelProvider', chatModelProvider);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
chatModelProvider &&
|
|
|
|
|
!chatModelProviders[chatModelProvider][chatModel]
|
|
|
|
|
) {
|
|
|
|
|
chatModel = Object.keys(
|
|
|
|
|
chatModelProviders[
|
|
|
|
|
Object.keys(chatModelProviders[chatModelProvider]).length > 0
|
|
|
|
|
? chatModelProvider
|
|
|
|
|
: Object.keys(chatModelProviders)[0]
|
|
|
|
|
],
|
|
|
|
|
)[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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setChatModelProvider({
|
|
|
|
|
name: chatModel!,
|
|
|
|
|
provider: chatModelProvider,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
setEmbeddingModelProvider({
|
|
|
|
|
name: embeddingModel!,
|
|
|
|
|
provider: embeddingModelProvider,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
setIsConfigReady(true);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('An error occurred while checking the configuration:', err);
|
|
|
|
|
setIsConfigReady(false);
|
|
|
|
|
setHasError(true);
|
|
|
|
|
}
|
2024-08-29 16:51:59 +05:30
|
|
|
};
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2024-06-29 11:09:51 +05:30
|
|
|
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 12:11:34 +05:30
|
|
|
setNotFound: (notFound: boolean) => void,
|
2024-11-23 15:04:19 +05:30
|
|
|
setFiles: (files: File[]) => void,
|
|
|
|
|
setFileIds: (fileIds: string[]) => void,
|
2024-06-29 11:09:51 +05:30
|
|
|
) => {
|
2025-03-19 13:41:52 +05:30
|
|
|
const res = await fetch(`/api/chats/${chatId}`, {
|
|
|
|
|
method: 'GET',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
2024-06-29 11:09:51 +05:30
|
|
|
},
|
2025-03-19 13:41:52 +05:30
|
|
|
});
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2024-06-29 12:11:34 +05:30
|
|
|
if (res.status === 404) {
|
2024-08-29 16:51:59 +05:30
|
|
|
setNotFound(true);
|
|
|
|
|
setIsMessagesLoaded(true);
|
|
|
|
|
return;
|
2024-06-29 12:11:34 +05:30
|
|
|
}
|
|
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
const data = await res.json();
|
2024-06-29 11:09:51 +05:30
|
|
|
|
|
|
|
|
const messages = data.messages.map((msg: any) => {
|
|
|
|
|
return {
|
|
|
|
|
...msg,
|
|
|
|
|
...JSON.parse(msg.metadata),
|
2024-08-29 16:51:59 +05:30
|
|
|
};
|
|
|
|
|
}) as Message[];
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
setMessages(messages);
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
const history = messages.map((msg) => {
|
|
|
|
|
return [msg.role, msg.content];
|
|
|
|
|
}) as [string, string][];
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2025-01-05 17:29:53 +00:00
|
|
|
console.debug(new Date(), 'app:messages_loaded');
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
document.title = messages[0].content;
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2024-11-23 15:04:19 +05:30
|
|
|
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-08-29 16:51:59 +05:30
|
|
|
setChatHistory(history);
|
|
|
|
|
setFocusMode(data.chat.focusMode);
|
|
|
|
|
setIsMessagesLoaded(true);
|
|
|
|
|
};
|
2024-06-29 11:09:51 +05:30
|
|
|
|
|
|
|
|
const ChatWindow = ({ id }: { id?: string }) => {
|
2024-08-29 16:51:59 +05:30
|
|
|
const searchParams = useSearchParams();
|
|
|
|
|
const initialMessage = searchParams.get('q');
|
2024-05-11 12:09:39 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
const [chatId, setChatId] = useState<string | undefined>(id);
|
|
|
|
|
const [newChatCreated, setNewChatCreated] = useState(false);
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2025-03-19 13:41:52 +05:30
|
|
|
const [chatModelProvider, setChatModelProvider] = useState<ChatModelProvider>(
|
|
|
|
|
{
|
|
|
|
|
name: '',
|
|
|
|
|
provider: '',
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const [embeddingModelProvider, setEmbeddingModelProvider] =
|
|
|
|
|
useState<EmbeddingModelProvider>({
|
|
|
|
|
name: '',
|
|
|
|
|
provider: '',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const [isConfigReady, setIsConfigReady] = useState(false);
|
2024-08-29 16:51:59 +05:30
|
|
|
const [hasError, setHasError] = useState(false);
|
|
|
|
|
const [isReady, setIsReady] = useState(false);
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2025-03-19 16:23:27 +05:30
|
|
|
useEffect(() => {
|
|
|
|
|
checkConfig(
|
|
|
|
|
setChatModelProvider,
|
|
|
|
|
setEmbeddingModelProvider,
|
|
|
|
|
setIsConfigReady,
|
|
|
|
|
setHasError,
|
|
|
|
|
);
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
|
}, []);
|
2024-05-11 12:09:39 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
|
const [messageAppeared, setMessageAppeared] = useState(false);
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
const [chatHistory, setChatHistory] = useState<[string, string][]>([]);
|
|
|
|
|
const [messages, setMessages] = useState<Message[]>([]);
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2024-11-23 15:04:19 +05:30
|
|
|
const [files, setFiles] = useState<File[]>([]);
|
|
|
|
|
const [fileIds, setFileIds] = useState<string[]>([]);
|
|
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
const [focusMode, setFocusMode] = useState('webSearch');
|
2024-10-11 10:35:59 +05:30
|
|
|
const [optimizationMode, setOptimizationMode] = useState('speed');
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
const [isMessagesLoaded, setIsMessagesLoaded] = useState(false);
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
const [notFound, setNotFound] = useState(false);
|
2024-06-29 12:11:34 +05:30
|
|
|
|
2024-06-29 11:09:51 +05:30
|
|
|
useEffect(() => {
|
|
|
|
|
if (
|
|
|
|
|
chatId &&
|
|
|
|
|
!newChatCreated &&
|
|
|
|
|
!isMessagesLoaded &&
|
|
|
|
|
messages.length === 0
|
|
|
|
|
) {
|
|
|
|
|
loadMessages(
|
|
|
|
|
chatId,
|
|
|
|
|
setMessages,
|
|
|
|
|
setIsMessagesLoaded,
|
|
|
|
|
setChatHistory,
|
|
|
|
|
setFocusMode,
|
2024-06-29 12:11:34 +05:30
|
|
|
setNotFound,
|
2024-11-23 15:04:19 +05:30
|
|
|
setFiles,
|
|
|
|
|
setFileIds,
|
2024-08-29 16:51:59 +05:30
|
|
|
);
|
2024-06-29 11:09:51 +05:30
|
|
|
} else if (!chatId) {
|
2024-08-29 16:51:59 +05:30
|
|
|
setNewChatCreated(true);
|
|
|
|
|
setIsMessagesLoaded(true);
|
|
|
|
|
setChatId(crypto.randomBytes(20).toString('hex'));
|
2024-06-29 11:09:51 +05:30
|
|
|
}
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2024-08-29 16:51:59 +05:30
|
|
|
}, []);
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
const messagesRef = useRef<Message[]>([]);
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2024-05-18 13:10:39 +05:30
|
|
|
useEffect(() => {
|
2024-08-29 16:51:59 +05:30
|
|
|
messagesRef.current = messages;
|
|
|
|
|
}, [messages]);
|
2024-05-18 13:10:39 +05:30
|
|
|
|
2024-06-29 11:09:51 +05:30
|
|
|
useEffect(() => {
|
2025-03-19 13:41:52 +05:30
|
|
|
if (isMessagesLoaded && isConfigReady) {
|
2024-08-29 16:51:59 +05:30
|
|
|
setIsReady(true);
|
2025-01-05 17:29:53 +00:00
|
|
|
console.debug(new Date(), 'app:ready');
|
2025-01-07 12:26:38 +05:30
|
|
|
} else {
|
|
|
|
|
setIsReady(false);
|
2024-06-29 11:09:51 +05:30
|
|
|
}
|
2025-03-19 13:41:52 +05:30
|
|
|
}, [isMessagesLoaded, isConfigReady]);
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2024-10-17 18:51:11 +05:30
|
|
|
const sendMessage = async (message: string, messageId?: string) => {
|
2024-08-29 16:51:59 +05:30
|
|
|
if (loading) return;
|
2025-03-19 13:41:52 +05:30
|
|
|
if (!isConfigReady) {
|
|
|
|
|
toast.error('Cannot send message before the configuration is ready');
|
2025-01-05 17:29:53 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2024-10-17 18:51:11 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
setLoading(true);
|
|
|
|
|
setMessageAppeared(false);
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
let sources: Document[] | undefined = undefined;
|
|
|
|
|
let recievedMessage = '';
|
|
|
|
|
let added = false;
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2024-10-17 18:51:11 +05:30
|
|
|
messageId = messageId ?? crypto.randomBytes(7).toString('hex');
|
2024-06-29 11:09:51 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
setMessages((prevMessages) => [
|
2024-04-09 16:21:05 +05:30
|
|
|
...prevMessages,
|
|
|
|
|
{
|
|
|
|
|
content: message,
|
2024-06-29 11:09:51 +05:30
|
|
|
messageId: messageId,
|
|
|
|
|
chatId: chatId!,
|
2024-04-09 16:21:05 +05:30
|
|
|
role: 'user',
|
2024-04-09 19:10:15 +05:30
|
|
|
createdAt: new Date(),
|
2024-04-09 16:21:05 +05:30
|
|
|
},
|
2024-08-29 16:51:59 +05:30
|
|
|
]);
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2025-03-19 13:41:52 +05:30
|
|
|
const messageHandler = async (data: any) => {
|
2024-05-04 14:56:54 +05:30
|
|
|
if (data.type === 'error') {
|
2024-08-29 16:51:59 +05:30
|
|
|
toast.error(data.data);
|
|
|
|
|
setLoading(false);
|
|
|
|
|
return;
|
2024-05-04 14:56:54 +05:30
|
|
|
}
|
|
|
|
|
|
2024-04-09 16:21:05 +05:30
|
|
|
if (data.type === 'sources') {
|
2024-08-29 16:51:59 +05:30
|
|
|
sources = data.data;
|
2024-04-09 16:21:05 +05:30
|
|
|
if (!added) {
|
2024-08-29 16:51:59 +05:30
|
|
|
setMessages((prevMessages) => [
|
2024-04-09 16:21:05 +05:30
|
|
|
...prevMessages,
|
|
|
|
|
{
|
|
|
|
|
content: '',
|
2024-06-29 11:09:51 +05:30
|
|
|
messageId: data.messageId,
|
|
|
|
|
chatId: chatId!,
|
2024-04-09 16:21:05 +05:30
|
|
|
role: 'assistant',
|
|
|
|
|
sources: sources,
|
2024-04-09 19:10:15 +05:30
|
|
|
createdAt: new Date(),
|
2024-04-09 16:21:05 +05:30
|
|
|
},
|
2024-08-29 16:51:59 +05:30
|
|
|
]);
|
|
|
|
|
added = true;
|
2024-04-09 16:21:05 +05:30
|
|
|
}
|
2024-08-29 16:51:59 +05:30
|
|
|
setMessageAppeared(true);
|
2024-04-09 16:21:05 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.type === 'message') {
|
|
|
|
|
if (!added) {
|
2024-08-29 16:51:59 +05:30
|
|
|
setMessages((prevMessages) => [
|
2024-04-09 16:21:05 +05:30
|
|
|
...prevMessages,
|
|
|
|
|
{
|
|
|
|
|
content: data.data,
|
2024-06-29 11:09:51 +05:30
|
|
|
messageId: data.messageId,
|
|
|
|
|
chatId: chatId!,
|
2024-04-09 16:21:05 +05:30
|
|
|
role: 'assistant',
|
|
|
|
|
sources: sources,
|
2024-04-09 19:10:15 +05:30
|
|
|
createdAt: new Date(),
|
2024-04-09 16:21:05 +05:30
|
|
|
},
|
2024-08-29 16:51:59 +05:30
|
|
|
]);
|
|
|
|
|
added = true;
|
2024-04-09 16:21:05 +05:30
|
|
|
}
|
|
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
setMessages((prev) =>
|
|
|
|
|
prev.map((message) => {
|
2024-06-29 11:09:51 +05:30
|
|
|
if (message.messageId === data.messageId) {
|
2024-08-29 16:51:59 +05:30
|
|
|
return { ...message, content: message.content + data.data };
|
2024-04-09 16:21:05 +05:30
|
|
|
}
|
|
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
return message;
|
2024-04-09 16:21:05 +05:30
|
|
|
}),
|
2024-08-29 16:51:59 +05:30
|
|
|
);
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
recievedMessage += data.data;
|
|
|
|
|
setMessageAppeared(true);
|
2024-04-09 16:21:05 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.type === 'messageEnd') {
|
2024-08-29 16:51:59 +05:30
|
|
|
setChatHistory((prevHistory) => [
|
2024-04-09 16:21:05 +05:30
|
|
|
...prevHistory,
|
|
|
|
|
['human', message],
|
|
|
|
|
['assistant', recievedMessage],
|
2024-08-29 16:51:59 +05:30
|
|
|
]);
|
2024-05-18 13:10:39 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
setLoading(false);
|
2024-05-18 13:10:39 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
const lastMsg = messagesRef.current[messagesRef.current.length - 1];
|
2024-05-18 13:10:39 +05:30
|
|
|
|
2025-03-20 09:47:32 +05:30
|
|
|
const autoImageSearch = localStorage.getItem('autoImageSearch');
|
|
|
|
|
const autoVideoSearch = localStorage.getItem('autoVideoSearch');
|
|
|
|
|
|
|
|
|
|
if (autoImageSearch === 'true') {
|
|
|
|
|
document
|
|
|
|
|
.getElementById(`search-images-${lastMsg.messageId}`)
|
|
|
|
|
?.click();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (autoVideoSearch === 'true') {
|
|
|
|
|
document
|
|
|
|
|
.getElementById(`search-videos-${lastMsg.messageId}`)
|
|
|
|
|
?.click();
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-18 13:10:39 +05:30
|
|
|
if (
|
|
|
|
|
lastMsg.role === 'assistant' &&
|
|
|
|
|
lastMsg.sources &&
|
|
|
|
|
lastMsg.sources.length > 0 &&
|
|
|
|
|
!lastMsg.suggestions
|
|
|
|
|
) {
|
2024-08-29 16:51:59 +05:30
|
|
|
const suggestions = await getSuggestions(messagesRef.current);
|
|
|
|
|
setMessages((prev) =>
|
|
|
|
|
prev.map((msg) => {
|
2024-06-29 11:09:51 +05:30
|
|
|
if (msg.messageId === lastMsg.messageId) {
|
2024-08-29 16:51:59 +05:30
|
|
|
return { ...msg, suggestions: suggestions };
|
2024-05-18 13:10:39 +05:30
|
|
|
}
|
2024-08-29 16:51:59 +05:30
|
|
|
return msg;
|
2024-05-18 13:10:39 +05:30
|
|
|
}),
|
2024-08-29 16:51:59 +05:30
|
|
|
);
|
2024-05-18 13:10:39 +05:30
|
|
|
}
|
2024-04-09 16:21:05 +05:30
|
|
|
}
|
2024-08-29 16:51:59 +05:30
|
|
|
};
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2025-03-19 13:41:52 +05:30
|
|
|
const res = await fetch('/api/chat', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
content: message,
|
|
|
|
|
message: {
|
|
|
|
|
messageId: messageId,
|
|
|
|
|
chatId: chatId!,
|
|
|
|
|
content: message,
|
|
|
|
|
},
|
|
|
|
|
chatId: chatId!,
|
|
|
|
|
files: fileIds,
|
|
|
|
|
focusMode: focusMode,
|
|
|
|
|
optimizationMode: optimizationMode,
|
|
|
|
|
history: chatHistory,
|
|
|
|
|
chatModel: {
|
|
|
|
|
name: chatModelProvider.name,
|
|
|
|
|
provider: chatModelProvider.provider,
|
|
|
|
|
},
|
|
|
|
|
embeddingModel: {
|
|
|
|
|
name: embeddingModelProvider.name,
|
|
|
|
|
provider: embeddingModelProvider.provider,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!res.body) throw new Error('No response body');
|
|
|
|
|
|
|
|
|
|
const reader = res.body?.getReader();
|
|
|
|
|
const decoder = new TextDecoder('utf-8');
|
|
|
|
|
|
|
|
|
|
let partialChunk = '';
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
const { value, done } = await reader.read();
|
|
|
|
|
if (done) break;
|
|
|
|
|
|
|
|
|
|
partialChunk += decoder.decode(value, { stream: true });
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const messages = partialChunk.split('\n');
|
|
|
|
|
for (const msg of messages) {
|
|
|
|
|
if (!msg.trim()) continue;
|
|
|
|
|
const json = JSON.parse(msg);
|
|
|
|
|
messageHandler(json);
|
|
|
|
|
}
|
|
|
|
|
partialChunk = '';
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.warn('Incomplete JSON, waiting for next chunk...');
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-29 16:51:59 +05:30
|
|
|
};
|
2024-04-09 16:21:05 +05:30
|
|
|
|
|
|
|
|
const rewrite = (messageId: string) => {
|
2024-08-29 16:51:59 +05:30
|
|
|
const index = messages.findIndex((msg) => msg.messageId === messageId);
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
if (index === -1) return;
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
const message = messages[index - 1];
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
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-04-09 16:21:05 +05:30
|
|
|
|
2024-10-17 18:51:11 +05:30
|
|
|
sendMessage(message.content, message.messageId);
|
2024-08-29 16:51:59 +05:30
|
|
|
};
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2024-05-11 12:09:39 +05:30
|
|
|
useEffect(() => {
|
2025-03-19 13:41:52 +05:30
|
|
|
if (isReady && initialMessage && isConfigReady) {
|
2024-08-29 16:51:59 +05:30
|
|
|
sendMessage(initialMessage);
|
2024-05-11 12:09:39 +05:30
|
|
|
}
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2025-03-19 13:41:52 +05:30
|
|
|
}, [isConfigReady, isReady, initialMessage]);
|
2024-05-11 12:09:39 +05:30
|
|
|
|
2024-06-25 15:43:36 +05:30
|
|
|
if (hasError) {
|
|
|
|
|
return (
|
2025-01-11 13:10:10 +05:30
|
|
|
<div className="relative">
|
|
|
|
|
<div className="absolute w-full flex flex-row items-center justify-end mr-5 mt-5">
|
2025-02-15 11:31:08 +05:30
|
|
|
<Link href="/settings">
|
|
|
|
|
<Settings className="cursor-pointer lg:hidden" />
|
|
|
|
|
</Link>
|
2025-01-11 13:10:10 +05:30
|
|
|
</div>
|
|
|
|
|
<div className="flex flex-col items-center justify-center min-h-screen">
|
|
|
|
|
<p className="dark:text-white/70 text-black/70 text-sm">
|
|
|
|
|
Failed to connect to the server. Please try again later.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
2024-06-25 15:43:36 +05:30
|
|
|
</div>
|
2024-08-29 16:51:59 +05:30
|
|
|
);
|
2024-06-25 15:43:36 +05:30
|
|
|
}
|
|
|
|
|
|
2024-05-11 12:09:39 +05:30
|
|
|
return isReady ? (
|
2024-06-29 12:11:34 +05:30
|
|
|
notFound ? (
|
2025-01-05 17:29:53 +00:00
|
|
|
<NextError statusCode={404} />
|
2024-06-29 12:11:34 +05:30
|
|
|
) : (
|
|
|
|
|
<div>
|
|
|
|
|
{messages.length > 0 ? (
|
|
|
|
|
<>
|
2024-10-29 19:59:58 +05:30
|
|
|
<Navbar chatId={chatId!} messages={messages} />
|
2024-06-29 12:11:34 +05:30
|
|
|
<Chat
|
|
|
|
|
loading={loading}
|
|
|
|
|
messages={messages}
|
|
|
|
|
sendMessage={sendMessage}
|
|
|
|
|
messageAppeared={messageAppeared}
|
|
|
|
|
rewrite={rewrite}
|
2024-11-23 15:04:19 +05:30
|
|
|
fileIds={fileIds}
|
|
|
|
|
setFileIds={setFileIds}
|
|
|
|
|
files={files}
|
|
|
|
|
setFiles={setFiles}
|
2024-06-29 12:11:34 +05:30
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<EmptyChat
|
2024-04-09 16:21:05 +05:30
|
|
|
sendMessage={sendMessage}
|
2024-06-29 12:11:34 +05:30
|
|
|
focusMode={focusMode}
|
|
|
|
|
setFocusMode={setFocusMode}
|
2024-10-11 10:35:59 +05:30
|
|
|
optimizationMode={optimizationMode}
|
|
|
|
|
setOptimizationMode={setOptimizationMode}
|
2024-11-23 15:04:19 +05:30
|
|
|
fileIds={fileIds}
|
|
|
|
|
setFileIds={setFileIds}
|
|
|
|
|
files={files}
|
|
|
|
|
setFiles={setFiles}
|
2024-04-09 16:21:05 +05:30
|
|
|
/>
|
2024-06-29 12:11:34 +05:30
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)
|
2024-04-20 10:23:56 +05:30
|
|
|
) : (
|
2024-08-29 16:51:59 +05:30
|
|
|
<div className="flex flex-row items-center justify-center min-h-screen">
|
2024-04-20 11:18:52 +05:30
|
|
|
<svg
|
2024-08-29 16:51:59 +05:30
|
|
|
aria-hidden="true"
|
|
|
|
|
className="w-8 h-8 text-light-200 fill-light-secondary dark:text-[#202020] animate-spin dark:fill-[#ffffff3b]"
|
|
|
|
|
viewBox="0 0 100 101"
|
|
|
|
|
fill="none"
|
|
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
2024-04-20 11:18:52 +05:30
|
|
|
>
|
|
|
|
|
<path
|
2024-08-29 16:51:59 +05:30
|
|
|
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"
|
|
|
|
|
fill="currentColor"
|
2024-04-20 11:18:52 +05:30
|
|
|
/>
|
|
|
|
|
<path
|
2024-08-29 16:51:59 +05:30
|
|
|
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"
|
|
|
|
|
fill="currentFill"
|
2024-04-20 11:18:52 +05:30
|
|
|
/>
|
2024-04-20 10:23:56 +05:30
|
|
|
</svg>
|
|
|
|
|
</div>
|
2024-08-29 16:51:59 +05:30
|
|
|
);
|
|
|
|
|
};
|
2024-04-09 16:21:05 +05:30
|
|
|
|
2024-08-29 16:51:59 +05:30
|
|
|
export default ChatWindow;
|