diff --git a/surfsense_web/app/dashboard/[search_space_id]/v2/[[...chat_id]]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/v2/[[...chat_id]]/page.tsx new file mode 100644 index 000000000..52ce7d881 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/v2/[[...chat_id]]/page.tsx @@ -0,0 +1,143 @@ +"use client"; + +import { useChat, Message, CreateMessage } from "@ai-sdk/react"; +import { useParams, useRouter } from "next/navigation"; +import { useEffect } from "react"; +import ChatMain from "@/components/chat_v2/ChatMain"; +import { ResearchMode } from "@/components/chat"; +import { useChatState, useChatAPI } from "@/hooks/useChat"; + +export default function ResearchChatPageV2() { + const { search_space_id, chat_id } = useParams(); + const router = useRouter(); + + const chatIdParam = Array.isArray(chat_id) ? chat_id[0] : chat_id; + const isNewChat = !chatIdParam; + + const { + token, + isLoading, + setIsLoading, + searchMode, + researchMode, + setResearchMode, + selectedConnectors, + setSelectedConnectors, + } = useChatState({ + search_space_id: search_space_id as string, + chat_id: chatIdParam, + }); + + const { fetchChatDetails, updateChat, createChat } = useChatAPI({ + token, + search_space_id: search_space_id as string, + researchMode, + selectedConnectors, + }); + + // Single useChat handler for both cases + const handler = useChat({ + api: `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chat`, + streamProtocol: "data", + initialMessages: [], + headers: { + ...(token && { Authorization: `Bearer ${token}` }), + }, + body: { + data: { + search_space_id: search_space_id, + selected_connectors: selectedConnectors, + research_mode: researchMode, + search_mode: searchMode, + document_ids_to_add_in_context: [], + }, + }, + onError: (error) => { + console.error("Chat error:", error); + }, + }); + + const customHandlerAppend = async ( + message: Message | CreateMessage, + chatRequestOptions?: { data?: any } + ) => { + const newChatId = await createChat(message.content); + router.replace(`/dashboard/${search_space_id}/v2/${newChatId}`); + return newChatId; + }; + + useEffect(() => { + if (token && !isNewChat && chatIdParam) { + setIsLoading(true); + loadChatData(chatIdParam); + } + }, [token, isNewChat, chatIdParam]); + + const loadChatData = async (chatId: string) => { + try { + const chatData = await fetchChatDetails(chatId); + if (!chatData) return; + + // Update configuration from chat data + if (chatData.type) { + setResearchMode(chatData.type as ResearchMode); + } + + if ( + chatData.initial_connectors && + Array.isArray(chatData.initial_connectors) + ) { + setSelectedConnectors(chatData.initial_connectors); + } + + // Load existing messages + if (chatData.messages && Array.isArray(chatData.messages)) { + if ( + chatData.messages.length === 1 && + chatData.messages[0].role === "user" + ) { + // Single user message - append to trigger LLM response + handler.append({ + role: "user", + content: chatData.messages[0].content, + }); + } else if (chatData.messages.length > 1) { + // Multiple messages - set them all + handler.setMessages(chatData.messages); + } + } + } finally { + setIsLoading(false); + } + }; + + // Auto-update chat when messages change (only for existing chats) + useEffect(() => { + if ( + !isNewChat && + chatIdParam && + handler.status === "ready" && + handler.messages.length > 0 && + handler.messages[handler.messages.length - 1]?.role === "assistant" + ) { + updateChat(chatIdParam, handler.messages); + } + }, [handler.messages, handler.status, chatIdParam, isNewChat, updateChat]); + + if (isLoading) { + return ( +
+
Loading...
+
+ ); + } + + return ( + + ); +} diff --git a/surfsense_web/app/dashboard/[search_space_id]/v2/[chat_id]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/v2/[chat_id]/page.tsx deleted file mode 100644 index ecb9e4cc2..000000000 --- a/surfsense_web/app/dashboard/[search_space_id]/v2/[chat_id]/page.tsx +++ /dev/null @@ -1,193 +0,0 @@ -"use client"; - -import { Message, useChat } from "@ai-sdk/react"; -import { useParams } from "next/navigation"; -import { useEffect, useState } from "react"; -import ChatMain from "@/components/chat_v2/ChatMain"; -import { ResearchMode } from "@/components/chat"; - -export default function ResearcherChatPageV2() { - const { search_space_id, chat_id } = useParams(); - - const [token, setToken] = useState(null); - const [isLoading, setIsLoading] = useState(false); - - // const [initialMessages, setInitialMessages] = useState([]); - - const [searchMode, setSearchMode] = useState<"DOCUMENTS" | "CHUNKS">( - "DOCUMENTS" - ); - const [researchMode, setResearchMode] = useState("QNA"); - const [selectedConnectors, setSelectedConnectors] = useState([]); - - const handler = useChat({ - api: `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chat`, - streamProtocol: "data", - initialMessages: [], - headers: { - ...(token && { Authorization: `Bearer ${token}` }), - }, - body: { - data: { - search_space_id: search_space_id, - selected_connectors: selectedConnectors, - research_mode: researchMode, - search_mode: searchMode, - document_ids_to_add_in_context: [], - }, - }, - onError: (error) => { - console.error("Chat error:", error); - // You can add additional error handling here if needed - }, - }); - - useEffect(() => { - setIsLoading(true); - let token = localStorage.getItem("surfsense_bearer_token"); - if (token) { - setToken(token); - fetchChatDetails(token); - setIsLoading(false); - } - }, [chat_id]); - - const fetchChatDetails = async (token: string) => { - try { - if (!token) return; - - // console.log('Fetching chat details for chat ID:', chat_id); - - const response = await fetch( - `${ - process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL - }/api/v1/chats/${Number(chat_id)}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - } - ); - - if (!response.ok) { - throw new Error( - `Failed to fetch chat details: ${response.statusText}` - ); - } - - const chatData = await response.json(); - // console.log('Chat details fetched:', chatData); - - // Set research mode from chat data - if (chatData.type) { - setResearchMode(chatData.type as ResearchMode); - } - - // Set connectors from chat data - if ( - chatData.initial_connectors && - Array.isArray(chatData.initial_connectors) - ) { - setSelectedConnectors(chatData.initial_connectors); - } - - if (chatData.messages && Array.isArray(chatData.messages)) { - console.log("chatData.messages", chatData.messages); - - if ( - chatData.messages.length === 1 && - chatData.messages[0].role === "user" - ) { - console.log("appending"); - handler.append({ - role: "user", - content: chatData.messages[0].content, - }); - } else { - console.log("setting"); - handler.setMessages(chatData.messages); - } - } - } catch (err) { - console.error("Error fetching chat details:", err); - } - }; - - const updateChat = async (messages: Message[]) => { - try { - const token = localStorage.getItem("surfsense_bearer_token"); - console.log("updating chat", messages, token); - if (!token) return; - - // Find the first user message to use as title - const userMessages = handler.messages.filter( - (msg: any) => msg.role === "user" - ); - - console.log("userMessages", userMessages); - console.log("handler.messages", handler.messages); - - if (userMessages.length === 0) return; - - // Use the first user message as the title - const title = userMessages[0].content; - - // console.log('Updating chat with title:', title); - - // Update the chat - console.log("messages", messages); - const response = await fetch( - `${ - process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL - }/api/v1/chats/${Number(chat_id)}`, - { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify({ - type: researchMode, - title: title, - initial_connectors: selectedConnectors, - messages: messages, - search_space_id: Number(search_space_id), - }), - } - ); - - if (!response.ok) { - throw new Error( - `Failed to update chat: ${response.statusText}` - ); - } - - // console.log('Chat updated successfully'); - } catch (err) { - console.error("Error updating chat:", err); - } - }; - - useEffect(() => { - console.log("handler.messages", handler.messages, handler.status); - if ( - handler.status === "ready" && - handler.messages.length > 0 && - handler.messages[handler.messages.length - 1]?.role === "assistant" - ) { - updateChat(handler.messages); - } - }, [handler.messages, handler.status]); - - const handleQuerySubmit = (input: string, handleSubmit: () => void) => { - handleSubmit(); - }; - - if (isLoading) { - return
Loading...
; - } - - return ; -} diff --git a/surfsense_web/app/dashboard/[search_space_id]/v2/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/v2/page.tsx deleted file mode 100644 index 5dd26e205..000000000 --- a/surfsense_web/app/dashboard/[search_space_id]/v2/page.tsx +++ /dev/null @@ -1,94 +0,0 @@ -"use client"; - -import { useChat } from "@ai-sdk/react"; - -import { useParams } from "next/navigation"; -import { useEffect, useState } from "react"; -import { useRouter } from "next/navigation"; -import ChatMain from "@/components/chat_v2/ChatMain"; - -export default function ResearcherPageV2() { - const { search_space_id, chat_id } = useParams(); - const router = useRouter(); - - const [token, setToken] = useState(null); - - useEffect(() => { - setToken(localStorage.getItem("surfsense_bearer_token")); - }, []); - - const handleQuerySubmit = (input: string, handleSubmit: () => void) => { - const createChat = async () => { - try { - if (!token) { - console.error("Authentication token not found"); - return; - } - - // Create a new chat - const response = await fetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats/`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify({ - type: "QNA", - title: "Untitled Chat", // Empty title initially - initial_connectors: [], // No default connectors - messages: [ - { - role: "user", - content: input, - }, - ], - search_space_id: Number(search_space_id), - }), - } - ); - - if (!response.ok) { - throw new Error( - `Failed to create chat: ${response.statusText}` - ); - } - - const data = await response.json(); - - router.replace(`/dashboard/${search_space_id}/v2/${data.id}`); - } catch (err) { - console.error("Error creating chat:", err); - } - }; - - if (!chat_id) { - createChat(); - return; - } - }; - - const handler = useChat({ - api: `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chat`, - streamProtocol: "data", - headers: { - ...(token && { Authorization: `Bearer ${token}` }), - }, - body: { - data: { - search_space_id: search_space_id, - selected_connectors: [], - research_mode: "QNA", - search_mode: "DOCUMENTS", - document_ids_to_add_in_context: [], - }, - }, - onError: (error) => { - console.error("Chat error:", error); - // You can add additional error handling here if needed - }, - }); - - return ; -} diff --git a/surfsense_web/components/chat_v2/ChatMain.tsx b/surfsense_web/components/chat_v2/ChatMain.tsx index 430c26f51..5e969ba99 100644 --- a/surfsense_web/components/chat_v2/ChatMain.tsx +++ b/surfsense_web/components/chat_v2/ChatMain.tsx @@ -1,75 +1,11 @@ "use client"; -import { - ChatCanvas, - ChatMessages, - ChatSection, - useChatUI, - ChatHandler, -} from "@llamaindex/chat-ui"; -import { Textarea } from "@/components/ui/textarea"; -import { Button } from "@/components/ui/button"; -import { Loader2 } from "lucide-react"; -import { useEffect } from "react"; +import { ChatSection, ChatHandler } from "@llamaindex/chat-ui"; interface ChatMainProps { handler: ChatHandler; - handleQuerySubmit: (input: string, handleSubmit: () => void) => void; } -const ChatInput = (props: { - handleQuerySubmit: (input: string, handleSubmit: () => void) => void; -}) => { - const { input, setInput, handleSubmit } = useChatUI(); - const { handleQuerySubmit } = props; - - const handleFormSubmit = (e: React.FormEvent) => { - e.preventDefault(); - if (!input.trim()) return; - - handleQuerySubmit(input, handleSubmit); - }; - - return ( -
-