2024-08-28 08:57:22 +00:00
|
|
|
'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'
|
|
|
|
import Error from 'next/error'
|
2024-04-09 10:51:05 +00:00
|
|
|
|
|
|
|
export type Message = {
|
2024-08-28 08:57:22 +00:00
|
|
|
messageId: string
|
|
|
|
chatId: string
|
|
|
|
createdAt: Date
|
|
|
|
content: string
|
|
|
|
role: 'user' | 'assistant'
|
|
|
|
suggestions?: string[]
|
|
|
|
sources?: Document[]
|
|
|
|
}
|
2024-04-09 10:51:05 +00:00
|
|
|
|
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-08-28 08:57:22 +00:00
|
|
|
const [ws, setWs] = useState<WebSocket | null>(null)
|
2024-04-09 10:51:05 +00:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (!ws) {
|
2024-05-02 06:44:26 +00:00
|
|
|
const connectWs = async () => {
|
2024-08-28 08:57:22 +00:00
|
|
|
let chatModel = localStorage.getItem('chatModel')
|
|
|
|
let chatModelProvider = localStorage.getItem('chatModelProvider')
|
|
|
|
let embeddingModel = localStorage.getItem('embeddingModel')
|
2024-05-04 05:21:06 +00:00
|
|
|
let embeddingModelProvider = localStorage.getItem(
|
|
|
|
'embeddingModelProvider',
|
2024-08-28 08:57:22 +00:00
|
|
|
)
|
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',
|
|
|
|
},
|
|
|
|
},
|
2024-08-28 08:57:22 +00:00
|
|
|
).then(async res => await res.json())
|
2024-08-04 12:44:46 +00:00
|
|
|
|
2024-05-04 05:21:06 +00:00
|
|
|
if (
|
|
|
|
!chatModel ||
|
|
|
|
!chatModelProvider ||
|
|
|
|
!embeddingModel ||
|
|
|
|
!embeddingModelProvider
|
|
|
|
) {
|
2024-08-04 12:44:46 +00:00
|
|
|
if (!chatModel || !chatModelProvider) {
|
2024-08-28 08:57:22 +00:00
|
|
|
const chatModelProviders = providers.chatModelProviders
|
2024-05-04 05:21:06 +00:00
|
|
|
|
2024-08-28 08:57:22 +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-08-25 09:38:47 +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-28 08:57:22 +00:00
|
|
|
)
|
|
|
|
setError(true)
|
|
|
|
return
|
2024-08-04 12:44:46 +00:00
|
|
|
} else {
|
2024-08-28 08:57:22 +00:00
|
|
|
chatModel = Object.keys(chatModelProviders[chatModelProvider])[0]
|
2024-08-04 12:44:46 +00:00
|
|
|
if (
|
|
|
|
!chatModelProviders ||
|
|
|
|
Object.keys(chatModelProviders).length === 0
|
|
|
|
)
|
2024-08-28 08:57:22 +00:00
|
|
|
return toast.error('No chat models available')
|
2024-08-04 12:44:46 +00:00
|
|
|
}
|
|
|
|
}
|
2024-07-25 15:06:26 +00:00
|
|
|
|
2024-08-04 12:44:46 +00:00
|
|
|
if (!embeddingModel || !embeddingModelProvider) {
|
2024-08-28 08:57:22 +00:00
|
|
|
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-28 08:57:22 +00:00
|
|
|
return toast.error('No embedding models available')
|
2024-07-25 15:06:26 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
embeddingModelProvider = Object.keys(embeddingModelProviders)[0]
|
2024-08-04 12:44:46 +00:00
|
|
|
embeddingModel = Object.keys(
|
|
|
|
embeddingModelProviders[embeddingModelProvider],
|
2024-08-28 08:57:22 +00:00
|
|
|
)[0]
|
2024-08-04 12:44:46 +00:00
|
|
|
}
|
2024-05-04 05:21:06 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
localStorage.setItem('chatModel', chatModel!)
|
|
|
|
localStorage.setItem('chatModelProvider', chatModelProvider)
|
|
|
|
localStorage.setItem('embeddingModel', embeddingModel!)
|
|
|
|
localStorage.setItem('embeddingModelProvider', embeddingModelProvider)
|
2024-07-09 10:51:45 +00:00
|
|
|
} else {
|
2024-08-28 08:57:22 +00:00
|
|
|
const chatModelProviders = providers.chatModelProviders
|
|
|
|
const embeddingModelProviders = providers.embeddingModelProviders
|
2024-07-09 10:51:45 +00:00
|
|
|
|
|
|
|
if (
|
|
|
|
Object.keys(chatModelProviders).length > 0 &&
|
|
|
|
!chatModelProviders[chatModelProvider]
|
|
|
|
) {
|
2024-08-28 08:57:22 +00:00
|
|
|
chatModelProvider = Object.keys(chatModelProviders)[0]
|
|
|
|
localStorage.setItem('chatModelProvider', chatModelProvider)
|
2024-07-09 10:51:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
chatModelProvider &&
|
2024-07-25 15:06:26 +00:00
|
|
|
chatModelProvider != 'custom_openai' &&
|
2024-07-09 10:51:45 +00:00
|
|
|
!chatModelProviders[chatModelProvider][chatModel]
|
|
|
|
) {
|
2024-08-28 08:57:22 +00:00
|
|
|
chatModel = Object.keys(chatModelProviders[chatModelProvider])[0]
|
|
|
|
localStorage.setItem('chatModel', chatModel)
|
2024-07-09 10:51:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
Object.keys(embeddingModelProviders).length > 0 &&
|
|
|
|
!embeddingModelProviders[embeddingModelProvider]
|
|
|
|
) {
|
2024-08-28 08:57:22 +00:00
|
|
|
embeddingModelProvider = Object.keys(embeddingModelProviders)[0]
|
2024-07-09 10:51:45 +00:00
|
|
|
localStorage.setItem(
|
|
|
|
'embeddingModelProvider',
|
|
|
|
embeddingModelProvider,
|
2024-08-28 08:57:22 +00:00
|
|
|
)
|
2024-07-09 10:51:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
embeddingModelProvider &&
|
|
|
|
!embeddingModelProviders[embeddingModelProvider][embeddingModel]
|
|
|
|
) {
|
|
|
|
embeddingModel = Object.keys(
|
|
|
|
embeddingModelProviders[embeddingModelProvider],
|
2024-08-28 08:57:22 +00:00
|
|
|
)[0]
|
|
|
|
localStorage.setItem('embeddingModel', embeddingModel)
|
2024-07-09 10:51:45 +00:00
|
|
|
}
|
2024-05-02 06:44:26 +00:00
|
|
|
}
|
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
const wsURL = new URL(url)
|
|
|
|
const searchParams = new URLSearchParams({})
|
2024-05-04 05:21:06 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
searchParams.append('chatModel', chatModel!)
|
|
|
|
searchParams.append('chatModelProvider', chatModelProvider)
|
2024-05-04 05:21:06 +00:00
|
|
|
|
|
|
|
if (chatModelProvider === 'custom_openai') {
|
|
|
|
searchParams.append(
|
|
|
|
'openAIApiKey',
|
|
|
|
localStorage.getItem('openAIApiKey')!,
|
2024-08-28 08:57:22 +00:00
|
|
|
)
|
2024-05-04 05:21:06 +00:00
|
|
|
searchParams.append(
|
|
|
|
'openAIBaseURL',
|
|
|
|
localStorage.getItem('openAIBaseURL')!,
|
2024-08-28 08:57:22 +00:00
|
|
|
)
|
2024-05-04 05:21:06 +00:00
|
|
|
}
|
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
searchParams.append('embeddingModel', embeddingModel!)
|
|
|
|
searchParams.append('embeddingModelProvider', embeddingModelProvider)
|
2024-05-04 05:21:06 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
wsURL.search = searchParams.toString()
|
2024-05-04 05:21:06 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
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-08-28 08:57:22 +00:00
|
|
|
)
|
2024-06-25 10:13:36 +00:00
|
|
|
}
|
2024-08-28 08:57:22 +00:00
|
|
|
}, 10000)
|
2024-06-25 10:13:36 +00:00
|
|
|
|
2024-05-02 06:44:26 +00:00
|
|
|
ws.onopen = () => {
|
2024-08-28 08:57:22 +00:00
|
|
|
console.log('[DEBUG] open')
|
|
|
|
clearTimeout(timeoutId)
|
|
|
|
setIsWSReady(true)
|
|
|
|
}
|
2024-05-04 09:26:54 +00:00
|
|
|
|
2024-06-25 10:13:36 +00:00
|
|
|
ws.onerror = () => {
|
2024-08-28 08:57:22 +00:00
|
|
|
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 = () => {
|
2024-08-28 08:57:22 +00:00
|
|
|
clearTimeout(timeoutId)
|
|
|
|
setError(true)
|
|
|
|
console.log('[DEBUG] closed')
|
|
|
|
}
|
2024-06-25 10:13:36 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
ws.addEventListener('message', e => {
|
|
|
|
const data = JSON.parse(e.data)
|
2024-07-25 15:06:26 +00:00
|
|
|
if (data.type === 'error') {
|
2024-08-28 08:57:22 +00:00
|
|
|
toast.error(data.data)
|
2024-07-25 15:06:26 +00:00
|
|
|
}
|
2024-08-28 08:57:22 +00:00
|
|
|
})
|
2024-07-25 15:06:26 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
setWs(ws)
|
|
|
|
}
|
2024-05-02 06:44:26 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
connectWs()
|
2024-04-09 10:51:05 +00:00
|
|
|
}
|
2024-08-28 08:57:22 +00:00
|
|
|
}, [ws, url, setIsWSReady, setError])
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
return ws
|
|
|
|
}
|
2024-04-09 10:51:05 +00:00
|
|
|
|
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-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-08-28 08:57:22 +00:00
|
|
|
)
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-06-29 06:41:34 +00:00
|
|
|
if (res.status === 404) {
|
2024-08-28 08:57:22 +00:00
|
|
|
setNotFound(true)
|
|
|
|
setIsMessagesLoaded(true)
|
|
|
|
return
|
2024-06-29 06:41:34 +00:00
|
|
|
}
|
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
const data = await res.json()
|
2024-06-29 05:39:51 +00:00
|
|
|
|
|
|
|
const messages = data.messages.map((msg: any) => {
|
|
|
|
return {
|
|
|
|
...msg,
|
|
|
|
...JSON.parse(msg.metadata),
|
2024-08-28 08:57:22 +00:00
|
|
|
}
|
|
|
|
}) as Message[]
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
setMessages(messages)
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
const history = messages.map(msg => {
|
|
|
|
return [msg.role, msg.content]
|
|
|
|
}) as [string, string][]
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
console.log('[DEBUG] messages loaded')
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
document.title = messages[0].content
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
setChatHistory(history)
|
|
|
|
setFocusMode(data.chat.focusMode)
|
|
|
|
setIsMessagesLoaded(true)
|
|
|
|
}
|
2024-06-29 05:39:51 +00:00
|
|
|
|
|
|
|
const ChatWindow = ({ id }: { id?: string }) => {
|
2024-08-28 08:57:22 +00:00
|
|
|
const searchParams = useSearchParams()
|
|
|
|
const initialMessage = searchParams.get('q')
|
2024-05-11 06:39:39 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
const [chatId, setChatId] = useState<string | undefined>(id)
|
|
|
|
const [newChatCreated, setNewChatCreated] = useState(false)
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
const [hasError, setHasError] = useState(false)
|
|
|
|
const [isReady, setIsReady] = useState(false)
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
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-08-28 08:57:22 +00:00
|
|
|
)
|
2024-05-11 06:39:39 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
const [messageAppeared, setMessageAppeared] = useState(false)
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
const [chatHistory, setChatHistory] = useState<[string, string][]>([])
|
|
|
|
const [messages, setMessages] = useState<Message[]>([])
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
const [focusMode, setFocusMode] = useState('webSearch')
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
const [isMessagesLoaded, setIsMessagesLoaded] = useState(false)
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
const [notFound, setNotFound] = useState(false)
|
2024-06-29 06:41:34 +00:00
|
|
|
|
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-08-28 08:57:22 +00:00
|
|
|
)
|
2024-06-29 05:39:51 +00:00
|
|
|
} else if (!chatId) {
|
2024-08-28 08:57:22 +00:00
|
|
|
setNewChatCreated(true)
|
|
|
|
setIsMessagesLoaded(true)
|
|
|
|
setChatId(crypto.randomBytes(20).toString('hex'))
|
2024-06-29 05:39:51 +00:00
|
|
|
}
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2024-08-28 08:57:22 +00:00
|
|
|
}, [])
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
useEffect(() => {
|
|
|
|
return () => {
|
|
|
|
if (ws?.readyState === 1) {
|
|
|
|
ws.close()
|
|
|
|
console.log('[DEBUG] closed')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
const messagesRef = useRef<Message[]>([])
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-05-18 07:40:39 +00:00
|
|
|
useEffect(() => {
|
2024-08-28 08:57:22 +00:00
|
|
|
messagesRef.current = messages
|
|
|
|
}, [messages])
|
2024-05-18 07:40:39 +00:00
|
|
|
|
2024-06-29 05:39:51 +00:00
|
|
|
useEffect(() => {
|
|
|
|
if (isMessagesLoaded && isWSReady) {
|
2024-08-28 08:57:22 +00:00
|
|
|
setIsReady(true)
|
2024-06-29 05:39:51 +00:00
|
|
|
}
|
2024-08-28 08:57:22 +00:00
|
|
|
}, [isMessagesLoaded, isWSReady])
|
2024-06-29 05:39:51 +00:00
|
|
|
|
2024-04-09 10:51:05 +00:00
|
|
|
const sendMessage = async (message: string) => {
|
2024-08-28 08:57:22 +00:00
|
|
|
if (loading) return
|
|
|
|
setLoading(true)
|
|
|
|
setMessageAppeared(false)
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
let sources: Document[] | undefined = undefined
|
|
|
|
let recievedMessage = ''
|
|
|
|
let added = false
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
const 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: {
|
|
|
|
chatId: chatId!,
|
|
|
|
content: message,
|
|
|
|
},
|
2024-04-13 06:41:47 +00:00
|
|
|
focusMode: focusMode,
|
2024-04-09 10:51:05 +00:00
|
|
|
history: [...chatHistory, ['human', message]],
|
|
|
|
}),
|
2024-08-28 08:57:22 +00:00
|
|
|
)
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
setMessages(prevMessages => [
|
2024-04-09 10:51:05 +00:00
|
|
|
...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-08-28 08:57:22 +00:00
|
|
|
])
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-05-18 07:40:39 +00:00
|
|
|
const messageHandler = async (e: MessageEvent) => {
|
2024-08-28 08:57:22 +00:00
|
|
|
const data = JSON.parse(e.data)
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-05-04 09:26:54 +00:00
|
|
|
if (data.type === 'error') {
|
2024-08-28 08:57:22 +00:00
|
|
|
toast.error(data.data)
|
|
|
|
setLoading(false)
|
|
|
|
return
|
2024-05-04 09:26:54 +00:00
|
|
|
}
|
|
|
|
|
2024-04-09 10:51:05 +00:00
|
|
|
if (data.type === 'sources') {
|
2024-08-28 08:57:22 +00:00
|
|
|
sources = data.data
|
2024-04-09 10:51:05 +00:00
|
|
|
if (!added) {
|
2024-08-28 08:57:22 +00:00
|
|
|
setMessages(prevMessages => [
|
2024-04-09 10:51:05 +00:00
|
|
|
...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
|
|
|
},
|
2024-08-28 08:57:22 +00:00
|
|
|
])
|
|
|
|
added = true
|
2024-04-09 10:51:05 +00:00
|
|
|
}
|
2024-08-28 08:57:22 +00:00
|
|
|
setMessageAppeared(true)
|
2024-04-09 10:51:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (data.type === 'message') {
|
|
|
|
if (!added) {
|
2024-08-28 08:57:22 +00:00
|
|
|
setMessages(prevMessages => [
|
2024-04-09 10:51:05 +00:00
|
|
|
...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
|
|
|
},
|
2024-08-28 08:57:22 +00:00
|
|
|
])
|
|
|
|
added = true
|
2024-04-09 10:51:05 +00:00
|
|
|
}
|
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
setMessages(prev =>
|
|
|
|
prev.map(message => {
|
2024-06-29 05:39:51 +00:00
|
|
|
if (message.messageId === data.messageId) {
|
2024-08-28 08:57:22 +00:00
|
|
|
return { ...message, content: message.content + data.data }
|
2024-04-09 10:51:05 +00:00
|
|
|
}
|
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
return message
|
2024-04-09 10:51:05 +00:00
|
|
|
}),
|
2024-08-28 08:57:22 +00:00
|
|
|
)
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
recievedMessage += data.data
|
|
|
|
setMessageAppeared(true)
|
2024-04-09 10:51:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (data.type === 'messageEnd') {
|
2024-08-28 08:57:22 +00:00
|
|
|
setChatHistory(prevHistory => [
|
2024-04-09 10:51:05 +00:00
|
|
|
...prevHistory,
|
|
|
|
['human', message],
|
|
|
|
['assistant', recievedMessage],
|
2024-08-28 08:57:22 +00:00
|
|
|
])
|
2024-05-18 07:40:39 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
ws?.removeEventListener('message', messageHandler)
|
|
|
|
setLoading(false)
|
2024-05-18 07:40:39 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
const lastMsg = messagesRef.current[messagesRef.current.length - 1]
|
2024-05-18 07:40:39 +00:00
|
|
|
|
|
|
|
if (
|
|
|
|
lastMsg.role === 'assistant' &&
|
|
|
|
lastMsg.sources &&
|
|
|
|
lastMsg.sources.length > 0 &&
|
|
|
|
!lastMsg.suggestions
|
|
|
|
) {
|
2024-08-28 08:57:22 +00:00
|
|
|
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-08-28 08:57:22 +00:00
|
|
|
return { ...msg, suggestions: suggestions }
|
2024-05-18 07:40:39 +00:00
|
|
|
}
|
2024-08-28 08:57:22 +00:00
|
|
|
return msg
|
2024-05-18 07:40:39 +00:00
|
|
|
}),
|
2024-08-28 08:57:22 +00:00
|
|
|
)
|
2024-05-18 07:40:39 +00:00
|
|
|
}
|
2024-04-09 10:51:05 +00:00
|
|
|
}
|
2024-08-28 08:57:22 +00:00
|
|
|
}
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
ws?.addEventListener('message', messageHandler)
|
|
|
|
}
|
2024-04-09 10:51:05 +00:00
|
|
|
|
|
|
|
const rewrite = (messageId: string) => {
|
2024-08-28 08:57:22 +00:00
|
|
|
const index = messages.findIndex(msg => msg.messageId === messageId)
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
if (index === -1) return
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
const message = messages[index - 1]
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
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 10:51:05 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
sendMessage(message.content)
|
|
|
|
}
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-05-11 06:39:39 +00:00
|
|
|
useEffect(() => {
|
|
|
|
if (isReady && initialMessage) {
|
2024-08-28 08:57:22 +00:00
|
|
|
sendMessage(initialMessage)
|
2024-05-11 06:39:39 +00:00
|
|
|
}
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2024-08-28 08:57:22 +00:00
|
|
|
}, [isReady, initialMessage])
|
2024-05-11 06:39:39 +00:00
|
|
|
|
2024-06-25 10:13:36 +00:00
|
|
|
if (hasError) {
|
|
|
|
return (
|
2024-08-28 08:57:22 +00:00
|
|
|
<div className='flex flex-col items-center justify-center min-h-screen'>
|
|
|
|
<p className='dark:text-white/70 text-black/70 text-sm'>
|
2024-06-25 10:41:39 +00:00
|
|
|
Failed to connect to the server. Please try again later.
|
|
|
|
</p>
|
2024-06-25 10:13:36 +00:00
|
|
|
</div>
|
2024-08-28 08:57:22 +00:00
|
|
|
)
|
2024-06-25 10:13:36 +00:00
|
|
|
}
|
|
|
|
|
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 ? (
|
|
|
|
<>
|
|
|
|
<Navbar messages={messages} />
|
|
|
|
<Chat
|
|
|
|
loading={loading}
|
|
|
|
messages={messages}
|
|
|
|
sendMessage={sendMessage}
|
|
|
|
messageAppeared={messageAppeared}
|
|
|
|
rewrite={rewrite}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
) : (
|
|
|
|
<EmptyChat
|
2024-04-09 10:51:05 +00:00
|
|
|
sendMessage={sendMessage}
|
2024-06-29 06:41:34 +00:00
|
|
|
focusMode={focusMode}
|
|
|
|
setFocusMode={setFocusMode}
|
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-08-28 08:57:22 +00:00
|
|
|
<div className='flex flex-row items-center justify-center min-h-screen'>
|
2024-04-20 05:48:52 +00:00
|
|
|
<svg
|
2024-08-28 08:57:22 +00:00
|
|
|
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 05:48:52 +00:00
|
|
|
>
|
|
|
|
<path
|
2024-08-28 08:57:22 +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'
|
|
|
|
fill='currentColor'
|
2024-04-20 05:48:52 +00:00
|
|
|
/>
|
|
|
|
<path
|
2024-08-28 08:57:22 +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'
|
|
|
|
fill='currentFill'
|
2024-04-20 05:48:52 +00:00
|
|
|
/>
|
2024-04-20 04:53:56 +00:00
|
|
|
</svg>
|
|
|
|
</div>
|
2024-08-28 08:57:22 +00:00
|
|
|
)
|
|
|
|
}
|
2024-04-09 10:51:05 +00:00
|
|
|
|
2024-08-28 08:57:22 +00:00
|
|
|
export default ChatWindow
|