From e33b469915a119ba105eedface77bd1701361813 Mon Sep 17 00:00:00 2001 From: Weves Date: Tue, 30 Apr 2024 11:01:55 -0700 Subject: [PATCH] Remove unused Chat.tsx file --- web/src/app/chat/Chat.tsx | 973 -------------------------------------- 1 file changed, 973 deletions(-) delete mode 100644 web/src/app/chat/Chat.tsx diff --git a/web/src/app/chat/Chat.tsx b/web/src/app/chat/Chat.tsx deleted file mode 100644 index 0d5a32756783..000000000000 --- a/web/src/app/chat/Chat.tsx +++ /dev/null @@ -1,973 +0,0 @@ -"use client"; - -import { useEffect, useRef, useState } from "react"; -import { FiSend, FiShare2, FiStopCircle } from "react-icons/fi"; -import { AIMessage, HumanMessage } from "./message/Messages"; -import { AnswerPiecePacket, DanswerDocument } from "@/lib/search/interfaces"; -import { - BackendChatSession, - BackendMessage, - ChatSessionSharedStatus, - DocumentsResponse, - Message, - RetrievalType, - StreamingError, -} from "./interfaces"; -import { useRouter, useSearchParams } from "next/navigation"; -import { FeedbackType } from "./types"; -import { - buildChatUrl, - createChatSession, - getCitedDocumentsFromMessage, - getHumanAndAIMessageFromMessageNumber, - getLastSuccessfulMessageId, - handleAutoScroll, - handleChatFeedback, - nameChatSession, - personaIncludesRetrieval, - processRawChatHistory, - sendMessage, -} from "./lib"; -import { ThreeDots } from "react-loader-spinner"; -import { FeedbackModal } from "./modal/FeedbackModal"; -import { DocumentSidebar } from "./documentSidebar/DocumentSidebar"; -import { ChatPersonaSelector } from "./ChatPersonaSelector"; -import { useFilters } from "@/lib/hooks"; -import { DocumentSet, Tag, ValidSources } from "@/lib/types"; -import { ChatFilters } from "./modifiers/ChatFilters"; -import { buildFilters } from "@/lib/search/utils"; -import { SelectedDocuments } from "./modifiers/SelectedDocuments"; -import { usePopup } from "@/components/admin/connectors/Popup"; -import { ResizableSection } from "@/components/resizable/ResizableSection"; -import { DanswerInitializingLoader } from "@/components/DanswerInitializingLoader"; -import { ChatIntro } from "./ChatIntro"; -import { computeAvailableFilters } from "@/lib/filters"; -import { useDocumentSelection } from "./useDocumentSelection"; -import { StarterMessage } from "./StarterMessage"; -import { ShareChatSessionModal } from "./modal/ShareChatSessionModal"; -import { SEARCH_PARAM_NAMES, shouldSubmitOnLoad } from "./searchParams"; -import { Persona } from "../admin/assistants/interfaces"; -import { ChatBanner } from "./ChatBanner"; -import { HEADER_PADDING } from "@/lib/constants"; - -const MAX_INPUT_HEIGHT = 200; - -export const Chat = ({ - existingChatSessionId, - existingChatSessionPersonaId, - availableSources, - availableDocumentSets, - availablePersonas, - availableTags, - defaultSelectedPersonaId, - documentSidebarInitialWidth, - shouldhideBeforeScroll, -}: { - existingChatSessionId: number | null; - existingChatSessionPersonaId: number | undefined; - availableSources: ValidSources[]; - availableDocumentSets: DocumentSet[]; - availablePersonas: Persona[]; - availableTags: Tag[]; - defaultSelectedPersonaId?: number; // what persona to default to - documentSidebarInitialWidth?: number; - shouldhideBeforeScroll?: boolean; -}) => { - const router = useRouter(); - const searchParams = useSearchParams(); - // used to track whether or not the initial "submit on load" has been performed - // this only applies if `?submit-on-load=true` or `?submit-on-load=1` is in the URL - // NOTE: this is required due to React strict mode, where all `useEffect` hooks - // are run twice on initial load during development - const submitOnLoadPerformed = useRef(false); - - const { popup, setPopup } = usePopup(); - - // fetch messages for the chat session - const [isFetchingChatMessages, setIsFetchingChatMessages] = useState( - existingChatSessionId !== null - ); - - // needed so closures (e.g. onSubmit) can access the current value - const urlChatSessionId = useRef(); - // this is triggered every time the user switches which chat - // session they are using - useEffect(() => { - urlChatSessionId.current = existingChatSessionId; - - textareaRef.current?.focus(); - - // only clear things if we're going from one chat session to another - if (chatSessionId !== null && existingChatSessionId !== chatSessionId) { - // de-select documents - clearSelectedDocuments(); - // reset all filters - filterManager.setSelectedDocumentSets([]); - filterManager.setSelectedSources([]); - filterManager.setSelectedTags([]); - filterManager.setTimeRange(null); - if (isStreaming) { - setIsCancelled(true); - } - } - - setChatSessionId(existingChatSessionId); - - async function initialSessionFetch() { - if (existingChatSessionId === null) { - setIsFetchingChatMessages(false); - if (defaultSelectedPersonaId !== undefined) { - setSelectedPersona( - availablePersonas.find( - (persona) => persona.id === defaultSelectedPersonaId - ) - ); - } else { - setSelectedPersona(undefined); - } - setMessageHistory([]); - setChatSessionSharedStatus(ChatSessionSharedStatus.Private); - - // if we're supposed to submit on initial load, then do that here - if ( - shouldSubmitOnLoad(searchParams) && - !submitOnLoadPerformed.current - ) { - submitOnLoadPerformed.current = true; - await onSubmit(); - } - - return; - } - - setIsFetchingChatMessages(true); - const response = await fetch( - `/api/chat/get-chat-session/${existingChatSessionId}` - ); - const chatSession = (await response.json()) as BackendChatSession; - setSelectedPersona( - availablePersonas.find( - (persona) => persona.id === chatSession.persona_id - ) - ); - - const newMessageHistory = processRawChatHistory(chatSession.messages); - setMessageHistory(newMessageHistory); - - const latestMessageId = - newMessageHistory[newMessageHistory.length - 1]?.messageId; - setSelectedMessageForDocDisplay( - latestMessageId !== undefined ? latestMessageId : null - ); - - setChatSessionSharedStatus(chatSession.shared_status); - - setIsFetchingChatMessages(false); - - // if this is a seeded chat, then kick off the AI message generation - if (newMessageHistory.length === 1 && !submitOnLoadPerformed.current) { - submitOnLoadPerformed.current = true; - const seededMessage = newMessageHistory[0].message; - await onSubmit({ - isSeededChat: true, - messageOverride: seededMessage, - }); - // force re-name if the chat session doesn't have one - if (!chatSession.description) { - await nameChatSession(existingChatSessionId, seededMessage); - router.refresh(); // need to refresh to update name on sidebar - } - } - } - - initialSessionFetch(); - }, [existingChatSessionId]); - - const [chatSessionId, setChatSessionId] = useState( - existingChatSessionId - ); - const [message, setMessage] = useState( - searchParams.get(SEARCH_PARAM_NAMES.USER_MESSAGE) || "" - ); - const [messageHistory, setMessageHistory] = useState([]); - const [isStreaming, setIsStreaming] = useState(false); - - // for document display - // NOTE: -1 is a special designation that means the latest AI message - const [selectedMessageForDocDisplay, setSelectedMessageForDocDisplay] = - useState(null); - const { aiMessage } = selectedMessageForDocDisplay - ? getHumanAndAIMessageFromMessageNumber( - messageHistory, - selectedMessageForDocDisplay - ) - : { aiMessage: null }; - - const [selectedPersona, setSelectedPersona] = useState( - existingChatSessionPersonaId !== undefined - ? availablePersonas.find( - (persona) => persona.id === existingChatSessionPersonaId - ) - : defaultSelectedPersonaId !== undefined - ? availablePersonas.find( - (persona) => persona.id === defaultSelectedPersonaId - ) - : undefined - ); - const livePersona = selectedPersona || availablePersonas[0]; - - const [chatSessionSharedStatus, setChatSessionSharedStatus] = - useState(ChatSessionSharedStatus.Private); - - useEffect(() => { - if (messageHistory.length === 0 && chatSessionId === null) { - setSelectedPersona( - availablePersonas.find( - (persona) => persona.id === defaultSelectedPersonaId - ) - ); - } - }, [defaultSelectedPersonaId]); - - const [ - selectedDocuments, - toggleDocumentSelection, - clearSelectedDocuments, - selectedDocumentTokens, - ] = useDocumentSelection(); - // just choose a conservative default, this will be updated in the - // background on initial load / on persona change - const [maxTokens, setMaxTokens] = useState(4096); - // fetch # of allowed document tokens for the selected Persona - useEffect(() => { - async function fetchMaxTokens() { - const response = await fetch( - `/api/chat/max-selected-document-tokens?persona_id=${livePersona.id}` - ); - if (response.ok) { - const maxTokens = (await response.json()).max_tokens as number; - setMaxTokens(maxTokens); - } - } - - fetchMaxTokens(); - }, [livePersona]); - - const filterManager = useFilters(); - const [finalAvailableSources, finalAvailableDocumentSets] = - computeAvailableFilters({ - selectedPersona, - availableSources, - availableDocumentSets, - }); - - // state for cancelling streaming - const [isCancelled, setIsCancelled] = useState(false); - const isCancelledRef = useRef(isCancelled); - useEffect(() => { - isCancelledRef.current = isCancelled; - }, [isCancelled]); - - const [currentFeedback, setCurrentFeedback] = useState< - [FeedbackType, number] | null - >(null); - const [sharingModalVisible, setSharingModalVisible] = - useState(false); - - // auto scroll as message comes out - const scrollableDivRef = useRef(null); - const endDivRef = useRef(null); - useEffect(() => { - if (isStreaming || !message) { - handleAutoScroll(endDivRef, scrollableDivRef); - } - }); - - // scroll to bottom initially - const [hasPerformedInitialScroll, setHasPerformedInitialScroll] = useState( - shouldhideBeforeScroll !== true - ); - useEffect(() => { - endDivRef.current?.scrollIntoView(); - setHasPerformedInitialScroll(true); - }, [isFetchingChatMessages]); - - // handle re-sizing of the text area - const textareaRef = useRef(null); - useEffect(() => { - const textarea = textareaRef.current; - if (textarea) { - textarea.style.height = "0px"; - textarea.style.height = `${Math.min( - textarea.scrollHeight, - MAX_INPUT_HEIGHT - )}px`; - } - }, [message]); - - // used for resizing of the document sidebar - const masterFlexboxRef = useRef(null); - const [maxDocumentSidebarWidth, setMaxDocumentSidebarWidth] = useState< - number | null - >(null); - const adjustDocumentSidebarWidth = () => { - if (masterFlexboxRef.current && document.documentElement.clientWidth) { - // numbers below are based on the actual width the center section for different - // screen sizes. `1700` corresponds to the custom "3xl" tailwind breakpoint - // NOTE: some buffer is needed to account for scroll bars - if (document.documentElement.clientWidth > 1700) { - setMaxDocumentSidebarWidth(masterFlexboxRef.current.clientWidth - 950); - } else if (document.documentElement.clientWidth > 1420) { - setMaxDocumentSidebarWidth(masterFlexboxRef.current.clientWidth - 760); - } else { - setMaxDocumentSidebarWidth(masterFlexboxRef.current.clientWidth - 660); - } - } - }; - useEffect(() => { - adjustDocumentSidebarWidth(); // Adjust the width on initial render - window.addEventListener("resize", adjustDocumentSidebarWidth); // Add resize event listener - - return () => { - window.removeEventListener("resize", adjustDocumentSidebarWidth); // Cleanup the event listener - }; - }, []); - - if (!documentSidebarInitialWidth && maxDocumentSidebarWidth) { - documentSidebarInitialWidth = Math.min(700, maxDocumentSidebarWidth); - } - - const onSubmit = async ({ - messageIdToResend, - messageOverride, - queryOverride, - forceSearch, - isSeededChat, - }: { - messageIdToResend?: number; - messageOverride?: string; - queryOverride?: string; - forceSearch?: boolean; - isSeededChat?: boolean; - } = {}) => { - let currChatSessionId: number; - let isNewSession = chatSessionId === null; - const searchParamBasedChatSessionName = - searchParams.get(SEARCH_PARAM_NAMES.TITLE) || null; - - if (isNewSession) { - currChatSessionId = await createChatSession( - livePersona?.id || 0, - searchParamBasedChatSessionName - ); - } else { - currChatSessionId = chatSessionId as number; - } - setChatSessionId(currChatSessionId); - - const messageToResend = messageHistory.find( - (message) => message.messageId === messageIdToResend - ); - const messageToResendIndex = messageToResend - ? messageHistory.indexOf(messageToResend) - : null; - if (!messageToResend && messageIdToResend !== undefined) { - setPopup({ - message: - "Failed to re-send message - please refresh the page and try again.", - type: "error", - }); - return; - } - - let currMessage = messageToResend ? messageToResend.message : message; - if (messageOverride) { - currMessage = messageOverride; - } - const currMessageHistory = - messageToResendIndex !== null - ? messageHistory.slice(0, messageToResendIndex) - : messageHistory; - setMessageHistory([ - ...currMessageHistory, - { - messageId: 0, - message: currMessage, - type: "user", - }, - ]); - setMessage(""); - - setIsStreaming(true); - let answer = ""; - let query: string | null = null; - let retrievalType: RetrievalType = - selectedDocuments.length > 0 - ? RetrievalType.SelectedDocs - : RetrievalType.None; - let documents: DanswerDocument[] = selectedDocuments; - let error: string | null = null; - let finalMessage: BackendMessage | null = null; - try { - const lastSuccessfulMessageId = - getLastSuccessfulMessageId(currMessageHistory); - for await (const packetBunch of sendMessage({ - message: currMessage, - parentMessageId: lastSuccessfulMessageId, - chatSessionId: currChatSessionId, - promptId: livePersona?.prompts[0]?.id || 0, - filters: buildFilters( - filterManager.selectedSources, - filterManager.selectedDocumentSets, - filterManager.timeRange, - filterManager.selectedTags - ), - selectedDocumentIds: selectedDocuments - .filter( - (document) => - document.db_doc_id !== undefined && document.db_doc_id !== null - ) - .map((document) => document.db_doc_id as number), - queryOverride, - forceSearch, - modelVersion: - searchParams.get(SEARCH_PARAM_NAMES.MODEL_VERSION) || undefined, - temperature: - parseFloat(searchParams.get(SEARCH_PARAM_NAMES.TEMPERATURE) || "") || - undefined, - systemPromptOverride: - searchParams.get(SEARCH_PARAM_NAMES.SYSTEM_PROMPT) || undefined, - useExistingUserMessage: isSeededChat, - })) { - for (const packet of packetBunch) { - if (Object.hasOwn(packet, "answer_piece")) { - answer += (packet as AnswerPiecePacket).answer_piece; - } else if (Object.hasOwn(packet, "top_documents")) { - documents = (packet as DocumentsResponse).top_documents; - query = (packet as DocumentsResponse).rephrased_query; - retrievalType = RetrievalType.Search; - if (documents && documents.length > 0) { - // point to the latest message (we don't know the messageId yet, which is why - // we have to use -1) - setSelectedMessageForDocDisplay(-1); - } - } else if (Object.hasOwn(packet, "error")) { - error = (packet as StreamingError).error; - } else if (Object.hasOwn(packet, "message_id")) { - finalMessage = packet as BackendMessage; - } - } - setMessageHistory([ - ...currMessageHistory, - { - messageId: finalMessage?.parent_message || null, - message: currMessage, - type: "user", - }, - { - messageId: finalMessage?.message_id || null, - message: error || answer, - type: error ? "error" : "assistant", - retrievalType, - query: finalMessage?.rephrased_query || query, - documents: finalMessage?.context_docs?.top_documents || documents, - citations: finalMessage?.citations || {}, - }, - ]); - if (isCancelledRef.current) { - setIsCancelled(false); - break; - } - } - } catch (e: any) { - const errorMsg = e.message; - setMessageHistory([ - ...currMessageHistory, - { - messageId: null, - message: currMessage, - type: "user", - }, - { - messageId: null, - message: errorMsg, - type: "error", - }, - ]); - } - setIsStreaming(false); - if (isNewSession) { - if (finalMessage) { - setSelectedMessageForDocDisplay(finalMessage.message_id); - } - if (!searchParamBasedChatSessionName) { - await nameChatSession(currChatSessionId, currMessage); - } - - // NOTE: don't switch pages if the user has navigated away from the chat - if ( - currChatSessionId === urlChatSessionId.current || - urlChatSessionId.current === null - ) { - router.push(buildChatUrl(searchParams, currChatSessionId, null), { - scroll: false, - }); - } - } - if ( - finalMessage?.context_docs && - finalMessage.context_docs.top_documents.length > 0 && - retrievalType === RetrievalType.Search - ) { - setSelectedMessageForDocDisplay(finalMessage.message_id); - } - }; - - const onFeedback = async ( - messageId: number, - feedbackType: FeedbackType, - feedbackDetails: string - ) => { - if (chatSessionId === null) { - return; - } - - const response = await handleChatFeedback( - messageId, - feedbackType, - feedbackDetails - ); - - if (response.ok) { - setPopup({ - message: "Thanks for your feedback!", - type: "success", - }); - } else { - const responseJson = await response.json(); - const errorMsg = responseJson.detail || responseJson.message; - setPopup({ - message: `Failed to submit feedback - ${errorMsg}`, - type: "error", - }); - } - }; - - const retrievalDisabled = !personaIncludesRetrieval(livePersona); - - return ( -
- {popup} - {currentFeedback && ( - setCurrentFeedback(null)} - onSubmit={(feedbackDetails) => { - onFeedback(currentFeedback[1], currentFeedback[0], feedbackDetails); - setCurrentFeedback(null); - }} - /> - )} - - {sharingModalVisible && chatSessionId !== null && ( - setSharingModalVisible(false)} - onShare={(shared) => - setChatSessionSharedStatus( - shared - ? ChatSessionSharedStatus.Public - : ChatSessionSharedStatus.Private - ) - } - /> - )} - - {documentSidebarInitialWidth !== undefined ? ( - <> -
-
- {/* ChatBanner is a custom banner that displays a admin-specified message at - the top of the chat page. Only used in the EE version of the app. */} - - - {livePersona && ( -
-
- { - if (persona) { - setSelectedPersona(persona); - textareaRef.current?.focus(); - router.push( - buildChatUrl(searchParams, null, persona.id) - ); - } - }} - /> -
- - {chatSessionId !== null && ( -
setSharingModalVisible(true)} - className="ml-auto mr-6 my-auto border-border border p-2 rounded cursor-pointer hover:bg-hover-light" - > - -
- )} -
- )} - - {messageHistory.length === 0 && - !isFetchingChatMessages && - !isStreaming && ( - { - setSelectedPersona(persona); - textareaRef.current?.focus(); - router.push(buildChatUrl(searchParams, null, persona.id)); - }} - /> - )} - -
- {messageHistory.map((message, i) => { - if (message.type === "user") { - return ( -
- -
- ); - } else if (message.type === "assistant") { - const isShowingRetrieved = - (selectedMessageForDocDisplay !== null && - selectedMessageForDocDisplay === message.messageId) || - (selectedMessageForDocDisplay === -1 && - i === messageHistory.length - 1); - const previousMessage = - i !== 0 ? messageHistory[i - 1] : null; - return ( -
- 0) === true - } - handleFeedback={ - i === messageHistory.length - 1 && isStreaming - ? undefined - : (feedbackType) => - setCurrentFeedback([ - feedbackType, - message.messageId as number, - ]) - } - handleSearchQueryEdit={ - i === messageHistory.length - 1 && !isStreaming - ? (newQuery) => { - if (!previousMessage) { - setPopup({ - type: "error", - message: - "Cannot edit query of first message - please refresh the page and try again.", - }); - return; - } - - if (previousMessage.messageId === null) { - setPopup({ - type: "error", - message: - "Cannot edit query of a pending message - please wait a few seconds and try again.", - }); - return; - } - onSubmit({ - messageIdToResend: - previousMessage.messageId, - queryOverride: newQuery, - }); - } - : undefined - } - isCurrentlyShowingRetrieved={isShowingRetrieved} - handleShowRetrieved={(messageNumber) => { - if (isShowingRetrieved) { - setSelectedMessageForDocDisplay(null); - } else { - if (messageNumber !== null) { - setSelectedMessageForDocDisplay(messageNumber); - } else { - setSelectedMessageForDocDisplay(-1); - } - } - }} - handleForceSearch={() => { - if (previousMessage && previousMessage.messageId) { - onSubmit({ - messageIdToResend: previousMessage.messageId, - forceSearch: true, - }); - } else { - setPopup({ - type: "error", - message: - "Failed to force search - please refresh the page and try again.", - }); - } - }} - retrievalDisabled={retrievalDisabled} - /> -
- ); - } else { - return ( -
- - {message.message} -

- } - /> -
- ); - } - })} - - {isStreaming && - messageHistory.length && - messageHistory[messageHistory.length - 1].type === "user" && ( -
- - -
- } - /> -
- )} - - {/* Some padding at the bottom so the search bar has space at the bottom to not cover the last message*/} -
- - {livePersona && - livePersona.starter_messages && - livePersona.starter_messages.length > 0 && - selectedPersona && - messageHistory.length === 0 && - !isFetchingChatMessages && ( -
- {livePersona.starter_messages.map((starterMessage, i) => ( -
- - onSubmit({ - messageOverride: starterMessage.message, - }) - } - /> -
- ))} -
- )} - -
-
-
- -
-
- {!retrievalDisabled && ( -
-
- {selectedDocuments.length > 0 ? ( - - ) : ( - - )} -
-
- )} - -
-
-