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条)