reactjs - Bot message erasing from chatHistory state on stop stream, but shows in state before stop - Stack Overflow

I have a stopStream function that will stop the stream of data coming to our gpt UI. On a new chat page

I have a stopStream function that will stop the stream of data coming to our gpt UI. On a new chat page, when I stop the first response, the response will disappear, but reappear on page load. When I console log chatHistory, before stopping the chatHistory has the user message and the bot message, but the log after the stop shows only the user message. So at some point, the bot message is being removed from chatHistory or it's being overwritten with just the user message. Any help is much appreciated. I have spent days on this.

import { useNavigate, useParams } from "react-router-dom";
import { useQueryClient } from "@tanstack/react-query";
import { cancelConversation, ChatMessageType, ChatRequest, fetchChatConversationByID, generateConversationTitle, streamChatApi, StreamResponse } from "@/api";
import QuestionInput from "@/components/QuestionInput/QuestionInput";
import { Loader2Icon } from "lucide-react";
import { ScrollWrapper } from "@/util/ScrollWrapper";
import { classNames } from "@/util/util";
import AskButton from "@/components/AskButton/AskButton";
import SuggestionList from "@/components/Suggestion/SuggestionList";
import { MemoizedChatMessage } from "@/components/ChatMessage/MemoizedChatMessage";
import { clearQuestion, loadQuestion, saveQuestion } from "@/questionStorage";
import usePersonaById from "@/hooks/usePersonaById";
import PersonaSelect from "@/components/Persona/PersonaSelect";
import useChatConversationById from "@/hooks/useChatConversationById";
import PersonaDescription from "@/components/Persona/PersonaDescription";
import useSuggestions from "@/hooks/useSuggestions";
import { useAppContext } from "@/context/AppContext";

const ChatConversationPage = () => {
    const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
    const [isLoading, setIsLoading] = useState<boolean>(false);
    const [chatStreaming, setChatStreaming] = useState<boolean>(false);
    const { personaId, setPersonaId } = useAppContext();
    const { conversationID } = useParams();
    const navigate = useNavigate();
    const queryClient = useQueryClient();
    const currentConversationID = useRef(conversationID);
    currentConversationID.current = conversationID;
    const { data: chatConversation = [] } = useChatConversationById(conversationID);
    const { data: persona, isLoading: isPersonaLoading } = usePersonaById(personaId);
    const { data: suggestions = [], isLoading: isSuggestionsLoading } = useSuggestions(persona?.instructions);
    const [abortController, setAbortController] = useState<AbortController | undefined>(undefined);

    const stopStream = async () => {
        if (abortController) {
            abortController.abort();
            setChatStreaming(false);
        }

        const idToCancel = currentConversationID.current || conversationID;
        await cancelConversation(idToCancel || "");
    };

    // updates the page with content being streamed in,
    // handling situations such as when the user navigates between conversations,
    // making sure to only update the content if on the correct screen.
    const handleAnswerStream = useCallback(
        async (stream: AsyncGenerator<StreamResponse, void>, userMessage: ChatMessageType, priorConversations: number) => {
            let answer = "";
            let id = conversationID;
            for await (let data of stream) {
                if (data.type === "id" && priorConversations === 0) {
                    // new conversation, update the id
                    id = data.id;
                } else if (data.type === "text") {
                    // append and update new text
                    answer += data.text;

                    const currentAnswer = answer;

                    // current view matches the conversation being streamed,
                    // update the displyed history with the contents.
                    if (currentConversationID.current === conversationID) {
                        setChatHistory((prev: ChatMessageType[]) => {
                            // create new message, needs to copy object to avoid issues anyway
                            const updatedMessage: ChatMessageType = { ...(prev ? prev[0] : userMessage), message: currentAnswer, type: "bot" };

                            const newMessages = [...prev];
                            if (prev.length === priorConversations) {
                                // prev missing user's message and updated message
                                newMessages.push(userMessage, updatedMessage);
                            } else if (prev.length === priorConversations + 1) {
                                // prev missing just updated message
                                newMessages.push(updatedMessage);
                            } else {
                                // prev has updated message, needs to be updated
                                newMessages[newMessages.length - 1] = updatedMessage;
                            }

                            return newMessages;
                        });
                    }
                }
            }
            return id;
        },
        [conversationID]
    );

    const fetchData = useCallback(async () => {
        setIsLoading(true);
        if (!conversationID) {
            setChatHistory([]);
        } else {
            try {
                const response = await fetchChatConversationByID(conversationID);
                setChatHistory(response);
            } catch {
                navigate("/");
            }
        }

        setIsLoading(false);
    }, [conversationID, navigate]);

    useEffect(() => {
        fetchData();
    }, [fetchData]);

    useEffect(() => {
        if (!conversationID) {
            setPersonaId("");
        }
    }, [conversationID, setPersonaId]);

    const handleQuestionSubmit = useCallback(
        async (question: string) => {
            const controller = new AbortController();
            setAbortController(controller);
            const signal = controller.signal;

            const request: ChatRequest = {
                message: question,
                prompt: chatConversation[0]?.persona?.instructions || persona?.instructions,
                personaId: persona?.personaId || chatConversation[0]?.persona?.personaId
            };

            const userQuestion: ChatMessageType = {
                type: "user",
                message: question,
                title: "",
                user: ""
            };

            setChatHistory((prev: ChatMessageType[]) => [...prev, userQuestion, { ...userQuestion, type: "bot", message: "" }]);

            try {
                setChatStreaming(true);
                const result = streamChatApi(request, signal, conversationID);
                const id = await handleAnswerStream(result, userQuestion, chatHistory.length);

                if (!id) throw new Error("No ID returned from API");

                if (!conversationID) {
                    saveQuestion(id, loadQuestion(conversationID));
                    clearQuestion(conversationID);
                }

                if (currentConversationID.current === conversationID && !conversationID) {
                    navigate(`/c/${id}`, { replace: true });
                }

                await generateConversationTitle(id);
                queryClient.invalidateQueries({ queryKey: ["conversation-titles-sidebar"] });
            } catch (error: any) {
                if (!(error instanceof Error)) throw new Error(error);
            } finally {
                setChatStreaming(false);
            }
        },
        [chatConversation, chatHistory.length, conversationID, handleAnswerStream, navigate, persona?.instructions, persona?.personaId, queryClient]
    );

    useEffect(() => {
        if (abortController) {
            abortController.abort();
        }

        setAbortController(undefined);

        setChatStreaming(false);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [conversationID]);

    if (!isLoading && chatHistory?.length === 0) {
        return (
            <div className="dark:bg-tenjin-dark-mode-primary flex flex-col h-full min-h-screen" data-testid="new-chat-conversation-wrapper">
                <PersonaSelect setPersonaId={setPersonaId} personaId={personaId} />
                <div className="flex flex-1 flex-col justify-center items-center text-tenjin-light-gray font-bold">
                    {!isPersonaLoading && (
                        <div className="flex flex-1 flex-col items-center justify-center">
                            <div className="flex flex-col justify-center items-center max-h-full pt-5 overflow-y-auto">
                                <PersonaDescription title={persona?.title} description={persona?.description} />
                                <SuggestionList
                                    persona={persona}
                                    isSuggestionsLoading={isSuggestionsLoading}
                                    handleClick={handleQuestionSubmit}
                                    suggestions={suggestions}
                                />
                            </div>
                        </div>
                    )}
                </div>

                <div className="flex flex-row gap-3 mx-4 last:mb-6 lg:mx-auto lg:max-w-2xl xl:max-w-3xl">
                    <div className="relative flex flex-1 flex-col">
                        <QuestionInput
                            persona={persona || chatConversation[0]?.persona}
                            onSend={handleQuestionSubmit}
                            disabled={false}
                            isAnswerStreaming={false}
                            conversationID={conversationID}
                            stopStream={stopStream}
                        />
                    </div>
                </div>
                <AskButton />
            </div>
        );
    }

    if (isLoading && chatHistory.length === 0) {
        return (
            <div className="dark:bg-tenjin-dark-mode-primary h-full w-full flex flex-col justify-between items-center">
                <div className="h-full w-full flex flex-col items-center">
                    <div className="flex flex-col h-full justify-center items-center">
                        <Loader2Icon className="animate-spin text-tenjin-dark-gray" />
                    </div>
                </div>
                <div className="flex flex-row justify-center w-full pt-4">
                    <QuestionInput
                        persona={persona || chatConversation[0]?.persona}
                        onSend={handleQuestionSubmit}
                        disabled={false}
                        isAnswerStreaming={chatStreaming}
                        placeholder="Send a message..."
                        conversationID={conversationID}
                        stopStream={stopStream}
                    />
                </div>
                <AskButton />
            </div>
        );
    }

    return (
        <div className="dark:bg-tenjin-dark-mode-primary h-full w-full flex flex-col justify-between z-0" data-testid="conversation-page-element">
            <ScrollWrapper
                content={chatHistory}
                personaTitle={chatConversation[0]?.persona?.title || persona?.title}
                className="relative w-full flex flex-col overflow-y-auto h-full scrollbar-thin scrollbar-thumb-tenjin-dark-gray scrollbar-track-tenjin-light-gray z-0"
            >
                {/* Chat history content (scrolls underneath) */}
                <div className="flex-1 z-0">
                    {chatHistory?.map((val, i) => (
                        <Fragment key={i}>
                            <MemoizedChatMessage {...val} />
                        </Fragment>
                    ))}
                </div>

                {/* Question input at the bottom */}
                <div
                    className={classNames(
                        "sticky bottom-0 mt-auto flex flex-row justify-center px-2 pb-2 md:pt-3 pt-2 border-t-2 border-tenjin-light-gray md:border-none dark:border-none",
                        "bg-white dark:bg-tenjin-dark-mode-primary md:bg-transparent md:bg-gradient-to-t from-white dark:from-tenjin-dark-mode-primary from-85% w-full"
                    )}
                >
                    <QuestionInput
                        persona={persona || chatConversation[0]?.persona}
                        onSend={handleQuestionSubmit}
                        isAnswerStreaming={chatStreaming}
                        disabled={chatStreaming}
                        placeholder="Send a message..."
                        conversationID={conversationID}
                        stopStream={stopStream}
                    />
                </div>
                <AskButton />
            </ScrollWrapper>
        </div>
    );
};

export default ChatConversationPage;```

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744836753a4596308.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信