Skip to content

Commit 501912c

Browse files
committed
feat: handle error when final chunk is received
1 parent 2721b61 commit 501912c

File tree

6 files changed

+59
-31
lines changed

6 files changed

+59
-31
lines changed

src/components/KymaCompanion/api/getChatResponse.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type GetChatResponseArgs = {
1515
resourceName?: string;
1616
sessionID: string;
1717
handleChatResponse: (chunk: MessageChunk) => void;
18-
handleError: (error?: Error) => void;
18+
handleError: (error?: string) => void;
1919
clusterUrl: string;
2020
clusterAuth: ClusterAuth;
2121
certificateAuthorityData: string;
@@ -87,7 +87,7 @@ function readChunk(
8787
reader: ReadableStreamDefaultReader<Uint8Array>,
8888
decoder: TextDecoder,
8989
handleChatResponse: (chunk: any) => void,
90-
handleError: (error?: Error) => void,
90+
handleError: (error?: string) => void,
9191
sessionID: string,
9292
) {
9393
reader
@@ -98,10 +98,6 @@ function readChunk(
9898
}
9999
const receivedString = decoder.decode(value, { stream: true });
100100
const chunk = JSON.parse(receivedString);
101-
if (chunk?.data?.error) {
102-
handleError(chunk.data.error);
103-
return;
104-
}
105101
handleChatResponse(chunk);
106102
readChunk(reader, decoder, handleChatResponse, handleError, sessionID);
107103
})

src/components/KymaCompanion/api/getFollowUpQuestions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { getClusterConfig } from 'state/utils/getBackendInfo';
33
interface GetFollowUpQuestionsParams {
44
sessionID?: string;
55
handleFollowUpQuestions: (results: any) => void;
6-
handleError: (error?: Error) => void;
6+
handleError: (error?: string) => void;
77
clusterUrl: string;
88
token: string;
99
certificateAuthorityData: string;

src/components/KymaCompanion/components/Chat/Chat.tsx

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { authDataState } from 'state/authDataAtom';
1111
import getFollowUpQuestions from 'components/KymaCompanion/api/getFollowUpQuestions';
1212
import getChatResponse from 'components/KymaCompanion/api/getChatResponse';
1313
import { usePromptSuggestions } from 'components/KymaCompanion/hooks/usePromptSuggestions';
14+
import { AIError } from '../KymaCompanion';
1415
import './Chat.scss';
1516

1617
export interface MessageType {
@@ -28,8 +29,8 @@ type ChatProps = {
2829
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
2930
isReset: boolean;
3031
setIsReset: React.Dispatch<React.SetStateAction<boolean>>;
31-
error: string | null;
32-
setError: React.Dispatch<React.SetStateAction<string | null>>;
32+
error: AIError;
33+
setError: React.Dispatch<React.SetStateAction<AIError>>;
3334
};
3435

3536
export const Chat = ({
@@ -78,18 +79,33 @@ export const Chat = ({
7879

7980
const handleChatResponse = (response: MessageChunk) => {
8081
const isLoading = response?.data?.answer?.next !== '__end__';
82+
8183
if (!isLoading) {
82-
setFollowUpLoading();
83-
getFollowUpQuestions({
84-
sessionID,
85-
handleFollowUpQuestions,
86-
handleError,
87-
clusterUrl: cluster.currentContext.cluster.cluster.server,
88-
token: authData.token,
89-
certificateAuthorityData:
90-
cluster.currentContext.cluster.cluster['certificate-authority-data'],
91-
});
84+
const finalTask = response.data.answer?.tasks?.at(-1);
85+
const hasError = finalTask?.status === 'error';
86+
87+
if (hasError) {
88+
const allTasksError =
89+
response.data.answer?.tasks?.every(task => task.status === 'error') ??
90+
false;
91+
const displayRetry = response.data.error !== null || allTasksError;
92+
handleError(response.data.answer.content, displayRetry);
93+
} else {
94+
setFollowUpLoading();
95+
getFollowUpQuestions({
96+
sessionID,
97+
handleFollowUpQuestions,
98+
handleError,
99+
clusterUrl: cluster.currentContext.cluster.cluster.server,
100+
token: authData.token,
101+
certificateAuthorityData:
102+
cluster.currentContext.cluster.cluster[
103+
'certificate-authority-data'
104+
],
105+
});
106+
}
92107
}
108+
93109
setChatHistory(prevMessages => {
94110
const [latestMessage] = prevMessages.slice(-1);
95111
return prevMessages.slice(0, -1).concat({
@@ -101,7 +117,7 @@ export const Chat = ({
101117
};
102118

103119
const setFollowUpLoading = () => {
104-
setError(null);
120+
setError({ message: null, displayRetry: false });
105121
setLoading(true);
106122
updateLatestMessage({ suggestionsLoading: true });
107123
};
@@ -111,14 +127,17 @@ export const Chat = ({
111127
setLoading(false);
112128
};
113129

114-
const handleError = (error?: Error) => {
115-
setError(error?.message ?? t('kyma-companion.error.subtitle'));
130+
const handleError = (error?: string, displayRetry?: boolean) => {
131+
setError({
132+
message: error ?? t('kyma-companion.error.subtitle'),
133+
displayRetry: displayRetry ?? false,
134+
});
116135
setChatHistory(prevItems => prevItems.slice(0, -1));
117136
setLoading(false);
118137
};
119138

120139
const sendPrompt = (query: string) => {
121-
setError(null);
140+
setError({ message: null, displayRetry: false });
122141
setLoading(true);
123142
addMessage({
124143
author: 'user',
@@ -249,11 +268,11 @@ export const Chat = ({
249268
/>
250269
);
251270
})}
252-
{error && (
271+
{error.message && (
253272
<ErrorMessage
254-
errorMessage={error}
255-
errorOnInitialMessage={chatHistory.length === 0}
273+
errorMessage={error.message ?? t('kyma-companion.error.subtitle')}
256274
retryPrompt={() => {}}
275+
displayRetry={error.displayRetry}
257276
/>
258277
)}
259278
</div>

src/components/KymaCompanion/components/Chat/messages/ErrorMessage.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { useTranslation } from 'react-i18next';
33

44
interface ErrorMessageProps {
55
errorMessage: string;
6-
errorOnInitialMessage: boolean;
6+
displayRetry: boolean;
77
retryPrompt: () => void;
88
}
99

1010
export default function ErrorMessage({
1111
errorMessage,
12-
errorOnInitialMessage,
12+
displayRetry,
1313
retryPrompt,
1414
}: ErrorMessageProps): JSX.Element {
1515
const { t } = useTranslation();
@@ -19,12 +19,13 @@ export default function ErrorMessage({
1919
<Card>
2020
<IllustratedMessage
2121
name="Connection"
22+
design="Spot"
2223
key="error-message"
2324
titleText={t('kyma-companion.error.title')}
2425
subtitleText={errorMessage}
2526
className="sap-margin-top-small no-padding"
2627
>
27-
{errorOnInitialMessage && (
28+
{displayRetry && (
2829
<Button
2930
onClick={retryPrompt}
3031
design="Emphasized"

src/components/KymaCompanion/components/Chat/messages/Message.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface MessageChunk {
2424
}[];
2525
next: string;
2626
};
27+
error?: string | null;
2728
};
2829
}
2930

src/components/KymaCompanion/components/KymaCompanion.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import {
99
import { Chat, MessageType } from './Chat/Chat';
1010
import './KymaCompanion.scss';
1111

12+
export interface AIError {
13+
message: string | null;
14+
displayRetry: boolean;
15+
}
16+
1217
export default function KymaCompanion() {
1318
const { t } = useTranslation();
1419

@@ -38,13 +43,19 @@ export default function KymaCompanion() {
3843
const [chatHistory, setChatHistory] = useState<MessageType[]>(
3944
initialChatHistory,
4045
);
41-
const [error, setError] = useState<string | null>(null);
46+
const [error, setError] = useState<AIError>({
47+
message: null,
48+
displayRetry: false,
49+
});
4250

4351
function handleRefresh() {
4452
setChatHistory(() => {
4553
return initialChatHistory;
4654
});
47-
setError(null);
55+
setError({
56+
message: null,
57+
displayRetry: false,
58+
});
4859
setIsReset(true);
4960
}
5061

0 commit comments

Comments
 (0)