refactor: replace isLoading with status (#861)

This commit is contained in:
Jeremy 2025-03-11 15:33:18 -07:00 committed by GitHub
parent 8e561dced4
commit 553a3d825a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 72 additions and 109 deletions

View file

@ -1,29 +1,26 @@
import { PreviewMessage } from './message'; import { PreviewMessage } from './message';
import { useScrollToBottom } from './use-scroll-to-bottom'; import { useScrollToBottom } from './use-scroll-to-bottom';
import { Vote } from '@/lib/db/schema'; import { Vote } from '@/lib/db/schema';
import { ChatRequestOptions, Message } from 'ai'; import { Message } from 'ai';
import { memo } from 'react'; import { memo } from 'react';
import equal from 'fast-deep-equal'; import equal from 'fast-deep-equal';
import { UIArtifact } from './artifact'; import { UIArtifact } from './artifact';
import { UseChatHelpers } from '@ai-sdk/react';
interface ArtifactMessagesProps { interface ArtifactMessagesProps {
chatId: string; chatId: string;
isLoading: boolean; status: UseChatHelpers['status'];
votes: Array<Vote> | undefined; votes: Array<Vote> | undefined;
messages: Array<Message>; messages: Array<Message>;
setMessages: ( setMessages: UseChatHelpers['setMessages'];
messages: Message[] | ((messages: Message[]) => Message[]), reload: UseChatHelpers['reload'];
) => void;
reload: (
chatRequestOptions?: ChatRequestOptions,
) => Promise<string | null | undefined>;
isReadonly: boolean; isReadonly: boolean;
artifactStatus: UIArtifact['status']; artifactStatus: UIArtifact['status'];
} }
function PureArtifactMessages({ function PureArtifactMessages({
chatId, chatId,
isLoading, status,
votes, votes,
messages, messages,
setMessages, setMessages,
@ -43,8 +40,7 @@ function PureArtifactMessages({
chatId={chatId} chatId={chatId}
key={message.id} key={message.id}
message={message} message={message}
isLoading={isLoading && index === messages.length - 1} isLoading={status === 'streaming' && index === messages.length - 1}
index={index}
vote={ vote={
votes votes
? votes.find((vote) => vote.messageId === message.id) ? votes.find((vote) => vote.messageId === message.id)
@ -74,8 +70,8 @@ function areEqual(
) )
return true; return true;
if (prevProps.isLoading !== nextProps.isLoading) return false; if (prevProps.status !== nextProps.status) return false;
if (prevProps.isLoading && nextProps.isLoading) return false; if (prevProps.status && nextProps.status) return false;
if (prevProps.messages.length !== nextProps.messages.length) return false; if (prevProps.messages.length !== nextProps.messages.length) return false;
if (!equal(prevProps.votes, nextProps.votes)) return false; if (!equal(prevProps.votes, nextProps.votes)) return false;

View file

@ -1,9 +1,4 @@
import type { import type { Attachment, Message } from 'ai';
Attachment,
ChatRequestOptions,
CreateMessage,
Message,
} from 'ai';
import { formatDistance } from 'date-fns'; import { formatDistance } from 'date-fns';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
import { import {
@ -31,6 +26,7 @@ import { codeArtifact } from '@/artifacts/code/client';
import { sheetArtifact } from '@/artifacts/sheet/client'; import { sheetArtifact } from '@/artifacts/sheet/client';
import { textArtifact } from '@/artifacts/text/client'; import { textArtifact } from '@/artifacts/text/client';
import equal from 'fast-deep-equal'; import equal from 'fast-deep-equal';
import { UseChatHelpers } from '@ai-sdk/react';
export const artifactDefinitions = [ export const artifactDefinitions = [
textArtifact, textArtifact,
@ -60,7 +56,7 @@ function PureArtifact({
input, input,
setInput, setInput,
handleSubmit, handleSubmit,
isLoading, status,
stop, stop,
attachments, attachments,
setAttachments, setAttachments,
@ -73,27 +69,17 @@ function PureArtifact({
}: { }: {
chatId: string; chatId: string;
input: string; input: string;
setInput: (input: string) => void; setInput: UseChatHelpers['setInput'];
isLoading: boolean; status: UseChatHelpers['status'];
stop: () => void; stop: UseChatHelpers['stop'];
attachments: Array<Attachment>; attachments: Array<Attachment>;
setAttachments: Dispatch<SetStateAction<Array<Attachment>>>; setAttachments: Dispatch<SetStateAction<Array<Attachment>>>;
messages: Array<Message>; messages: Array<Message>;
setMessages: Dispatch<SetStateAction<Array<Message>>>; setMessages: Dispatch<SetStateAction<Array<Message>>>;
votes: Array<Vote> | undefined; votes: Array<Vote> | undefined;
append: ( append: UseChatHelpers['append'];
message: Message | CreateMessage, handleSubmit: UseChatHelpers['handleSubmit'];
chatRequestOptions?: ChatRequestOptions, reload: UseChatHelpers['reload'];
) => Promise<string | null | undefined>;
handleSubmit: (
event?: {
preventDefault?: () => void;
},
chatRequestOptions?: ChatRequestOptions,
) => void;
reload: (
chatRequestOptions?: ChatRequestOptions,
) => Promise<string | null | undefined>;
isReadonly: boolean; isReadonly: boolean;
}) { }) {
const { artifact, setArtifact, metadata, setMetadata } = useArtifact(); const { artifact, setArtifact, metadata, setMetadata } = useArtifact();
@ -326,7 +312,7 @@ function PureArtifact({
<div className="flex flex-col h-full justify-between items-center gap-4"> <div className="flex flex-col h-full justify-between items-center gap-4">
<ArtifactMessages <ArtifactMessages
chatId={chatId} chatId={chatId}
isLoading={isLoading} status={status}
votes={votes} votes={votes}
messages={messages} messages={messages}
setMessages={setMessages} setMessages={setMessages}
@ -341,7 +327,7 @@ function PureArtifact({
input={input} input={input}
setInput={setInput} setInput={setInput}
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
isLoading={isLoading} status={status}
stop={stop} stop={stop}
attachments={attachments} attachments={attachments}
setAttachments={setAttachments} setAttachments={setAttachments}
@ -487,7 +473,7 @@ function PureArtifact({
isToolbarVisible={isToolbarVisible} isToolbarVisible={isToolbarVisible}
setIsToolbarVisible={setIsToolbarVisible} setIsToolbarVisible={setIsToolbarVisible}
append={append} append={append}
isLoading={isLoading} status={status}
stop={stop} stop={stop}
setMessages={setMessages} setMessages={setMessages}
artifactKind={artifact.kind} artifactKind={artifact.kind}
@ -513,7 +499,7 @@ function PureArtifact({
} }
export const Artifact = memo(PureArtifact, (prevProps, nextProps) => { export const Artifact = memo(PureArtifact, (prevProps, nextProps) => {
if (prevProps.isLoading !== nextProps.isLoading) return false; if (prevProps.status !== nextProps.status) return false;
if (!equal(prevProps.votes, nextProps.votes)) return false; if (!equal(prevProps.votes, nextProps.votes)) return false;
if (prevProps.input !== nextProps.input) return false; if (prevProps.input !== nextProps.input) return false;
if (!equal(prevProps.messages, nextProps.messages.length)) return false; if (!equal(prevProps.messages, nextProps.messages.length)) return false;

View file

@ -36,7 +36,7 @@ export function Chat({
input, input,
setInput, setInput,
append, append,
isLoading, status,
stop, stop,
reload, reload,
} = useChat({ } = useChat({
@ -74,7 +74,7 @@ export function Chat({
<Messages <Messages
chatId={id} chatId={id}
isLoading={isLoading} status={status}
votes={votes} votes={votes}
messages={messages} messages={messages}
setMessages={setMessages} setMessages={setMessages}
@ -90,7 +90,7 @@ export function Chat({
input={input} input={input}
setInput={setInput} setInput={setInput}
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
isLoading={isLoading} status={status}
stop={stop} stop={stop}
attachments={attachments} attachments={attachments}
setAttachments={setAttachments} setAttachments={setAttachments}
@ -107,7 +107,7 @@ export function Chat({
input={input} input={input}
setInput={setInput} setInput={setInput}
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
isLoading={isLoading} status={status}
stop={stop} stop={stop}
attachments={attachments} attachments={attachments}
setAttachments={setAttachments} setAttachments={setAttachments}

View file

@ -27,7 +27,6 @@ const PurePreviewMessage = ({
setMessages, setMessages,
reload, reload,
isReadonly, isReadonly,
index,
}: { }: {
chatId: string; chatId: string;
message: Message; message: Message;
@ -40,7 +39,6 @@ const PurePreviewMessage = ({
chatRequestOptions?: ChatRequestOptions, chatRequestOptions?: ChatRequestOptions,
) => Promise<string | null | undefined>; ) => Promise<string | null | undefined>;
isReadonly: boolean; isReadonly: boolean;
index: number;
}) => { }) => {
const [mode, setMode] = useState<'view' | 'edit'>('view'); const [mode, setMode] = useState<'view' | 'edit'>('view');

View file

@ -1,29 +1,26 @@
import { ChatRequestOptions, Message } from 'ai'; import { Message } from 'ai';
import { PreviewMessage, ThinkingMessage } from './message'; import { PreviewMessage, ThinkingMessage } from './message';
import { useScrollToBottom } from './use-scroll-to-bottom'; import { useScrollToBottom } from './use-scroll-to-bottom';
import { Overview } from './overview'; import { Overview } from './overview';
import { memo } from 'react'; import { memo } from 'react';
import { Vote } from '@/lib/db/schema'; import { Vote } from '@/lib/db/schema';
import equal from 'fast-deep-equal'; import equal from 'fast-deep-equal';
import { UseChatHelpers } from '@ai-sdk/react';
interface MessagesProps { interface MessagesProps {
chatId: string; chatId: string;
isLoading: boolean; status: UseChatHelpers['status'];
votes: Array<Vote> | undefined; votes: Array<Vote> | undefined;
messages: Array<Message>; messages: Array<Message>;
setMessages: ( setMessages: UseChatHelpers['setMessages'];
messages: Message[] | ((messages: Message[]) => Message[]), reload: UseChatHelpers['reload'];
) => void;
reload: (
chatRequestOptions?: ChatRequestOptions,
) => Promise<string | null | undefined>;
isReadonly: boolean; isReadonly: boolean;
isArtifactVisible: boolean; isArtifactVisible: boolean;
} }
function PureMessages({ function PureMessages({
chatId, chatId,
isLoading, status,
votes, votes,
messages, messages,
setMessages, setMessages,
@ -43,10 +40,9 @@ function PureMessages({
{messages.map((message, index) => ( {messages.map((message, index) => (
<PreviewMessage <PreviewMessage
key={message.id} key={message.id}
index={index}
chatId={chatId} chatId={chatId}
message={message} message={message}
isLoading={isLoading && messages.length - 1 === index} isLoading={status === 'streaming' && messages.length - 1 === index}
vote={ vote={
votes votes
? votes.find((vote) => vote.messageId === message.id) ? votes.find((vote) => vote.messageId === message.id)
@ -58,7 +54,7 @@ function PureMessages({
/> />
))} ))}
{isLoading && {status === 'submitted' &&
messages.length > 0 && messages.length > 0 &&
messages[messages.length - 1].role === 'user' && <ThinkingMessage />} messages[messages.length - 1].role === 'user' && <ThinkingMessage />}
@ -73,8 +69,8 @@ function PureMessages({
export const Messages = memo(PureMessages, (prevProps, nextProps) => { export const Messages = memo(PureMessages, (prevProps, nextProps) => {
if (prevProps.isArtifactVisible && nextProps.isArtifactVisible) return true; if (prevProps.isArtifactVisible && nextProps.isArtifactVisible) return true;
if (prevProps.isLoading !== nextProps.isLoading) return false; if (prevProps.status !== nextProps.status) return false;
if (prevProps.isLoading && nextProps.isLoading) return false; if (prevProps.status && nextProps.status) return false;
if (prevProps.messages.length !== nextProps.messages.length) return false; if (prevProps.messages.length !== nextProps.messages.length) return false;
if (!equal(prevProps.messages, nextProps.messages)) return false; if (!equal(prevProps.messages, nextProps.messages)) return false;
if (!equal(prevProps.votes, nextProps.votes)) return false; if (!equal(prevProps.votes, nextProps.votes)) return false;

View file

@ -29,12 +29,13 @@ import { Button } from './ui/button';
import { Textarea } from './ui/textarea'; import { Textarea } from './ui/textarea';
import { SuggestedActions } from './suggested-actions'; import { SuggestedActions } from './suggested-actions';
import equal from 'fast-deep-equal'; import equal from 'fast-deep-equal';
import { UseChatHelpers, UseChatOptions } from '@ai-sdk/react';
function PureMultimodalInput({ function PureMultimodalInput({
chatId, chatId,
input, input,
setInput, setInput,
isLoading, status,
stop, stop,
attachments, attachments,
setAttachments, setAttachments,
@ -45,24 +46,16 @@ function PureMultimodalInput({
className, className,
}: { }: {
chatId: string; chatId: string;
input: string; input: UseChatHelpers['input'];
setInput: (value: string) => void; setInput: UseChatHelpers['setInput'];
isLoading: boolean; status: UseChatHelpers['status'];
stop: () => void; stop: () => void;
attachments: Array<Attachment>; attachments: Array<Attachment>;
setAttachments: Dispatch<SetStateAction<Array<Attachment>>>; setAttachments: Dispatch<SetStateAction<Array<Attachment>>>;
messages: Array<Message>; messages: Array<Message>;
setMessages: Dispatch<SetStateAction<Array<Message>>>; setMessages: Dispatch<SetStateAction<Array<Message>>>;
append: ( append: UseChatHelpers['append'];
message: Message | CreateMessage, handleSubmit: UseChatHelpers['handleSubmit'];
chatRequestOptions?: ChatRequestOptions,
) => Promise<string | null | undefined>;
handleSubmit: (
event?: {
preventDefault?: () => void;
},
chatRequestOptions?: ChatRequestOptions,
) => void;
className?: string; className?: string;
}) { }) {
const textareaRef = useRef<HTMLTextAreaElement>(null); const textareaRef = useRef<HTMLTextAreaElement>(null);
@ -253,7 +246,7 @@ function PureMultimodalInput({
) { ) {
event.preventDefault(); event.preventDefault();
if (isLoading) { if (status !== 'ready') {
toast.error('Please wait for the model to finish its response!'); toast.error('Please wait for the model to finish its response!');
} else { } else {
submitForm(); submitForm();
@ -263,11 +256,11 @@ function PureMultimodalInput({
/> />
<div className="absolute bottom-0 p-2 w-fit flex flex-row justify-start"> <div className="absolute bottom-0 p-2 w-fit flex flex-row justify-start">
<AttachmentsButton fileInputRef={fileInputRef} isLoading={isLoading} /> <AttachmentsButton fileInputRef={fileInputRef} status={status} />
</div> </div>
<div className="absolute bottom-0 right-0 p-2 w-fit flex flex-row justify-end"> <div className="absolute bottom-0 right-0 p-2 w-fit flex flex-row justify-end">
{isLoading ? ( {status === 'submitted' ? (
<StopButton stop={stop} setMessages={setMessages} /> <StopButton stop={stop} setMessages={setMessages} />
) : ( ) : (
<SendButton <SendButton
@ -285,7 +278,7 @@ export const MultimodalInput = memo(
PureMultimodalInput, PureMultimodalInput,
(prevProps, nextProps) => { (prevProps, nextProps) => {
if (prevProps.input !== nextProps.input) return false; if (prevProps.input !== nextProps.input) return false;
if (prevProps.isLoading !== nextProps.isLoading) return false; if (prevProps.status !== nextProps.status) return false;
if (!equal(prevProps.attachments, nextProps.attachments)) return false; if (!equal(prevProps.attachments, nextProps.attachments)) return false;
return true; return true;
@ -294,10 +287,10 @@ export const MultimodalInput = memo(
function PureAttachmentsButton({ function PureAttachmentsButton({
fileInputRef, fileInputRef,
isLoading, status,
}: { }: {
fileInputRef: React.MutableRefObject<HTMLInputElement | null>; fileInputRef: React.MutableRefObject<HTMLInputElement | null>;
isLoading: boolean; status: UseChatHelpers['status'];
}) { }) {
return ( return (
<Button <Button
@ -307,7 +300,7 @@ function PureAttachmentsButton({
event.preventDefault(); event.preventDefault();
fileInputRef.current?.click(); fileInputRef.current?.click();
}} }}
disabled={isLoading} disabled={status !== 'ready'}
variant="ghost" variant="ghost"
> >
<PaperclipIcon size={14} /> <PaperclipIcon size={14} />

View file

@ -319,19 +319,16 @@ const PureToolbar = ({
isToolbarVisible, isToolbarVisible,
setIsToolbarVisible, setIsToolbarVisible,
append, append,
isLoading, status,
stop, stop,
setMessages, setMessages,
artifactKind, artifactKind,
}: { }: {
isToolbarVisible: boolean; isToolbarVisible: boolean;
setIsToolbarVisible: Dispatch<SetStateAction<boolean>>; setIsToolbarVisible: Dispatch<SetStateAction<boolean>>;
isLoading: boolean; status: UseChatHelpers['status'];
append: ( append: UseChatHelpers['append'];
message: Message | CreateMessage, stop: UseChatHelpers['stop'];
chatRequestOptions?: ChatRequestOptions,
) => Promise<string | null | undefined>;
stop: () => void;
setMessages: Dispatch<SetStateAction<Message[]>>; setMessages: Dispatch<SetStateAction<Message[]>>;
artifactKind: ArtifactKind; artifactKind: ArtifactKind;
}) => { }) => {
@ -372,10 +369,10 @@ const PureToolbar = ({
}, []); }, []);
useEffect(() => { useEffect(() => {
if (isLoading) { if (status === 'streaming') {
setIsToolbarVisible(false); setIsToolbarVisible(false);
} }
}, [isLoading, setIsToolbarVisible]); }, [status, setIsToolbarVisible]);
const artifactDefinition = artifactDefinitions.find( const artifactDefinition = artifactDefinitions.find(
(definition) => definition.kind === artifactKind, (definition) => definition.kind === artifactKind,
@ -418,13 +415,13 @@ const PureToolbar = ({
exit={{ opacity: 0, y: -20, transition: { duration: 0.1 } }} exit={{ opacity: 0, y: -20, transition: { duration: 0.1 } }}
transition={{ type: 'spring', stiffness: 300, damping: 25 }} transition={{ type: 'spring', stiffness: 300, damping: 25 }}
onHoverStart={() => { onHoverStart={() => {
if (isLoading) return; if (status === 'streaming') return;
cancelCloseTimer(); cancelCloseTimer();
setIsToolbarVisible(true); setIsToolbarVisible(true);
}} }}
onHoverEnd={() => { onHoverEnd={() => {
if (isLoading) return; if (status === 'streaming') return;
startCloseTimer(); startCloseTimer();
}} }}
@ -436,7 +433,7 @@ const PureToolbar = ({
}} }}
ref={toolbarRef} ref={toolbarRef}
> >
{isLoading ? ( {status === 'streaming' ? (
<motion.div <motion.div
key="stop-icon" key="stop-icon"
initial={{ scale: 1 }} initial={{ scale: 1 }}
@ -475,7 +472,7 @@ const PureToolbar = ({
}; };
export const Toolbar = memo(PureToolbar, (prevProps, nextProps) => { export const Toolbar = memo(PureToolbar, (prevProps, nextProps) => {
if (prevProps.isLoading !== nextProps.isLoading) return false; if (prevProps.status !== nextProps.status) return false;
if (prevProps.isToolbarVisible !== nextProps.isToolbarVisible) return false; if (prevProps.isToolbarVisible !== nextProps.isToolbarVisible) return false;
if (prevProps.artifactKind !== nextProps.artifactKind) return false; if (prevProps.artifactKind !== nextProps.artifactKind) return false;

View file

@ -11,6 +11,8 @@ export const chatModel = new MockLanguageModelV1({
}), }),
doStream: async ({ prompt }) => ({ doStream: async ({ prompt }) => ({
stream: simulateReadableStream({ stream: simulateReadableStream({
chunkDelayInMs: 50,
initialDelayInMs: 100,
chunks: getResponseChunksByPrompt(prompt), chunks: getResponseChunksByPrompt(prompt),
}), }),
rawCall: { rawPrompt: null, rawSettings: {} }, rawCall: { rawPrompt: null, rawSettings: {} },
@ -26,6 +28,8 @@ export const reasoningModel = new MockLanguageModelV1({
}), }),
doStream: async ({ prompt }) => ({ doStream: async ({ prompt }) => ({
stream: simulateReadableStream({ stream: simulateReadableStream({
chunkDelayInMs: 50,
initialDelayInMs: 500,
chunks: getResponseChunksByPrompt(prompt, true), chunks: getResponseChunksByPrompt(prompt, true),
}), }),
rawCall: { rawPrompt: null, rawSettings: {} }, rawCall: { rawPrompt: null, rawSettings: {} },
@ -41,6 +45,8 @@ export const titleModel = new MockLanguageModelV1({
}), }),
doStream: async () => ({ doStream: async () => ({
stream: simulateReadableStream({ stream: simulateReadableStream({
chunkDelayInMs: 50,
initialDelayInMs: 100,
chunks: [ chunks: [
{ type: 'text-delta', textDelta: 'This is a test title' }, { type: 'text-delta', textDelta: 'This is a test title' },
{ {
@ -64,6 +70,8 @@ export const artifactModel = new MockLanguageModelV1({
}), }),
doStream: async ({ prompt }) => ({ doStream: async ({ prompt }) => ({
stream: simulateReadableStream({ stream: simulateReadableStream({
chunkDelayInMs: 50,
initialDelayInMs: 100,
chunks: getResponseChunksByPrompt(prompt), chunks: getResponseChunksByPrompt(prompt),
}), }),
rawCall: { rawPrompt: null, rawSettings: {} }, rawCall: { rawPrompt: null, rawSettings: {} },

View file

@ -179,15 +179,4 @@ export class ChatPage {
}, },
}; };
} }
async waitForMessageGeneration(timeout = 10000) {
await this.page.waitForFunction(
() => {
return document.querySelector('[data-testid="send-button"]') !== null;
},
{ timeout },
);
await this.page.waitForTimeout(500);
}
} }

View file

@ -11,7 +11,7 @@ test.describe('chat activity with reasoning', () => {
test('send user message and generate response with reasoning', async () => { test('send user message and generate response with reasoning', async () => {
await chatPage.sendUserMessage('Why is the sky blue?'); await chatPage.sendUserMessage('Why is the sky blue?');
await chatPage.waitForMessageGeneration(); await chatPage.isGenerationComplete();
const assistantMessage = await chatPage.getRecentAssistantMessage(); const assistantMessage = await chatPage.getRecentAssistantMessage();
expect(assistantMessage.content).toBe("It's just blue duh!"); expect(assistantMessage.content).toBe("It's just blue duh!");
@ -23,7 +23,7 @@ test.describe('chat activity with reasoning', () => {
test('toggle reasoning visibility', async () => { test('toggle reasoning visibility', async () => {
await chatPage.sendUserMessage('Why is the sky blue?'); await chatPage.sendUserMessage('Why is the sky blue?');
await chatPage.waitForMessageGeneration(); await chatPage.isGenerationComplete();
const assistantMessage = await chatPage.getRecentAssistantMessage(); const assistantMessage = await chatPage.getRecentAssistantMessage();
const reasoningElement = const reasoningElement =
@ -39,7 +39,7 @@ test.describe('chat activity with reasoning', () => {
test('edit message and resubmit', async () => { test('edit message and resubmit', async () => {
await chatPage.sendUserMessage('Why is the sky blue?'); await chatPage.sendUserMessage('Why is the sky blue?');
await chatPage.waitForMessageGeneration(); await chatPage.isGenerationComplete();
const assistantMessage = await chatPage.getRecentAssistantMessage(); const assistantMessage = await chatPage.getRecentAssistantMessage();
const reasoningElement = const reasoningElement =
@ -49,7 +49,7 @@ test.describe('chat activity with reasoning', () => {
const userMessage = await chatPage.getRecentUserMessage(); const userMessage = await chatPage.getRecentUserMessage();
await userMessage.edit('Why is grass green?'); await userMessage.edit('Why is grass green?');
await chatPage.waitForMessageGeneration(); await chatPage.isGenerationComplete();
const updatedAssistantMessage = await chatPage.getRecentAssistantMessage(); const updatedAssistantMessage = await chatPage.getRecentAssistantMessage();