Skip to content

Commit

Permalink
Add loading indicator to chat transcript.
Browse files Browse the repository at this point in the history
  • Loading branch information
lublagg committed Dec 12, 2024
1 parent 73c48b4 commit 9bd4cf0
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 13 deletions.
1 change: 1 addition & 0 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ export const App = observer(() => {
<ChatTranscriptComponent
chatTranscript={transcriptStore}
showDebugLog={showDebugLog}
isLoading={assistantStore.isLoadingResponse}
/>
{isDevMode &&
<div className="show-debug-controls">
Expand Down
22 changes: 22 additions & 0 deletions src/components/chat-transcript.scss
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,28 @@
}
}
}
.loading:after {
overflow: hidden;
display: inline-block;
vertical-align: bottom;
-webkit-animation: ellipsis steps(4, end) 900ms infinite;
animation: ellipsis steps(4, end) 900ms infinite;
/* ascii code for the ellipsis character */
content: "\2026";
width: 0;
}

@keyframes ellipsis {
to {
width: 15px;
}
}

@-webkit-keyframes ellipsis {
to {
width: 15px;
}
}
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/components/chat-transcript.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import React, { useRef, useEffect } from "react";
import { observer } from "mobx-react-lite";
import { ChatTranscriptMessage } from "./chat-transcript-message";
import { ChatTranscript, ChatMessage } from "../types";
import { LoadingMessage } from "./loading-message";

import "./chat-transcript.scss";

interface IProps {
chatTranscript: ChatTranscript;
showDebugLog: boolean;
isLoading?: boolean;
}

export const ChatTranscriptComponent = observer(({chatTranscript, showDebugLog}: IProps) => {
export const ChatTranscriptComponent = observer(({chatTranscript, showDebugLog, isLoading}: IProps) => {
const chatTranscriptRef = useRef<HTMLElement>(null);

useEffect(() => {
Expand Down Expand Up @@ -41,6 +43,7 @@ export const ChatTranscriptComponent = observer(({chatTranscript, showDebugLog}:
/>
);
})}
{isLoading && <LoadingMessage />}
</section>
</section>
);
Expand Down
30 changes: 30 additions & 0 deletions src/components/loading-message.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from "react";
import { DAVAI_SPEAKER } from "../constants";
import { timeStamp } from "../utils/utils";

export const LoadingMessage = () => {
return (
<section
aria-label={`${DAVAI_SPEAKER} at ${timeStamp()}`}
className={`chat-transcript__message ${DAVAI_SPEAKER.toLowerCase()}`}
data-testid="chat-message"
role="listitem"
>
<h3 aria-label="speaker" data-testid="chat-message-speaker">
{DAVAI_SPEAKER}
</h3>
<div
aria-label="message"
className={`chat-message-content ${DAVAI_SPEAKER.toLowerCase()}`}
data-testid="chat-message-content"
>
<div
aria-label="Loading response, please wait"
className="loading"
data-testid="loading" role="status" aria-live="polite"
>
</div>
</div>
</section>
);
};
28 changes: 16 additions & 12 deletions src/models/assistant-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export const AssistantModel = types
transcriptStore: ChatTranscriptModel,
useExisting: true,
})
.volatile(() => ({
isLoadingResponse: false,
}))
.actions((self) => ({
handleMessageSubmitMockAssistant() {
// Use a brief delay to prevent duplicate timestamp-based keys.
Expand Down Expand Up @@ -85,7 +88,7 @@ export const AssistantModel = types
role: "user",
content: messageText,
});

self.isLoadingResponse = true;
self.transcriptStore.addMessage(DEBUG_SPEAKER, {description: "Message sent to LLM", content: formatJsonMessage(messageSent)});
yield startRun();

Expand All @@ -100,7 +103,6 @@ export const AssistantModel = types
const run = yield self.apiConnection.beta.threads.runs.create(self.thread.id, {
assistant_id: self.assistant.id,
});

yield pollRunState(run.id);
} catch (err) {
console.error("Failed to complete run:", err);
Expand Down Expand Up @@ -129,16 +131,6 @@ export const AssistantModel = types
});
}

if (errorStates.includes(runState.status)) {
self.transcriptStore.addMessage(DEBUG_SPEAKER, {
description: "Run failed",
content: formatJsonMessage(runState),
});
self.transcriptStore.addMessage(DAVAI_SPEAKER, {
content: "I'm sorry, I encountered an error. Please try again.",
});
}

if (runState.status === "requires_action") {
self.transcriptStore.addMessage(DEBUG_SPEAKER, {
description: "Run requires action",
Expand Down Expand Up @@ -168,6 +160,18 @@ export const AssistantModel = types
content: "I'm sorry, I don't have a response for that.",
});
}
self.isLoadingResponse = false;
}

if (errorStates.includes(runState.status)) {
self.transcriptStore.addMessage(DEBUG_SPEAKER, {
description: "Run failed",
content: formatJsonMessage(runState),
});
self.transcriptStore.addMessage(DAVAI_SPEAKER, {
content: "I'm sorry, I encountered an error. Please try again.",
});
self.isLoadingResponse = false;
}
});

Expand Down

0 comments on commit 9bd4cf0

Please sign in to comment.