Skip to content

Commit 4234d64

Browse files
authored
fix: Handle partial response error correctly (#4051)
* fix: hotfix, buffer incomplete JSON objects before parsing * test: adjust mocked response to exactly equal real-world response * fix: small adjustment to backend error handling * fix: display backend's errr titlw * add test cases for buffered streaming in edge cases * fix: context label being covered by code panel * fix: move helpers to separate file * fix: display error notice and full responses for partial AI failure * test: cover code changes in tests * fix: code clean-up, bug fix and increased test coverage
1 parent 282357b commit 4234d64

File tree

7 files changed

+393
-255
lines changed

7 files changed

+393
-255
lines changed

public/i18n/en.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,7 @@ kyma-companion:
786786
internal_server_error-message: Couldn't fetch response from Joule because of network errors.
787787
chat-error: An error occurred
788788
suggestions-error: No suggestions available
789+
partial-ai-failure: Some tasks encountered errors. The results might be incomplete or not fully actionable.
789790
http-error: Response status code is {{statusCode}}. Retrying {{attempt}}.
790791
http-error-no-retry: Response status code is {{statusCode}}.
791792
input-tokens:

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

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ import {
2222
ErrorType,
2323
MessageChunk,
2424
Message as MessageType,
25-
chatGroupHelpers,
2625
} from './types';
26+
import { chatHelpers } from './chatHelper';
2727
import './Chat.scss';
2828
import FeedbackMessage from './FeedbackMessage/FeedbackMessage';
2929

@@ -80,13 +80,13 @@ export const Chat = ({
8080
const addMessage = (message: MessageType) => {
8181
const currentContext = getCurrentContext();
8282
setChatHistory(prevGroups =>
83-
chatGroupHelpers.addMessage(prevGroups, message, currentContext),
83+
chatHelpers.addMessage(prevGroups, message, currentContext),
8484
);
8585
};
8686

8787
const updateLatestMessage = (updates: Partial<MessageType>) => {
8888
setChatHistory(prevGroups =>
89-
chatGroupHelpers.updateLatestMessage(prevGroups, updates),
89+
chatHelpers.updateLatestMessage(prevGroups, updates),
9090
);
9191
};
9292

@@ -96,7 +96,7 @@ export const Chat = ({
9696
isFeedback: boolean,
9797
) => {
9898
setChatHistory(prevGroups =>
99-
chatGroupHelpers.concatMsgToLatestMessage(
99+
chatHelpers.concatMsgToLatestMessage(
100100
prevGroups,
101101
response,
102102
isLoading,
@@ -106,39 +106,53 @@ export const Chat = ({
106106
};
107107

108108
const removeLastMessage = () => {
109-
setChatHistory(prevGroups =>
110-
chatGroupHelpers.removeLastMessage(prevGroups),
111-
);
109+
setChatHistory(prevGroups => chatHelpers.removeLastMessage(prevGroups));
112110
};
113111

114112
const setErrorOnLastUserMsg = () => {
115-
setChatHistory(prevGroups =>
116-
chatGroupHelpers.setErrorOnLastUserMsg(prevGroups),
117-
);
113+
setChatHistory(prevGroups => chatHelpers.setErrorOnLastUserMsg(prevGroups));
118114
};
119115

120116
const handleChatResponse = (response: MessageChunk) => {
121117
const isLoading = response?.data?.answer?.next !== '__end__';
122118
const isFeedback = response?.data?.answer?.is_feedback === true;
123119

124120
if (!isLoading) {
125-
const finalTask = response.data.answer?.tasks?.at(-1);
126-
const hasError = finalTask?.status === 'error';
121+
const hasError =
122+
response.data.answer?.tasks?.some(task => task.status === 'error') ??
123+
false;
127124

128125
if (hasError) {
129126
const allTasksError =
130127
response.data.answer?.tasks?.every(task => task.status === 'error') ??
131128
false;
132-
const displayRetry = response.data.error !== null || allTasksError;
133-
removeLastMessage();
134-
handleError(
135-
{
136-
type: ErrorType.FATAL,
137-
message: response.data.answer.content,
138-
},
139-
displayRetry,
140-
);
141-
return;
129+
if (!allTasksError) {
130+
// handle partial error
131+
updateLatestMessage({ partialAIFailure: true });
132+
setFollowUpLoading();
133+
getFollowUpQuestions({
134+
sessionID,
135+
handleFollowUpQuestions,
136+
handleFollowUpError,
137+
clusterUrl: cluster.currentContext.cluster.cluster.server,
138+
token: authData.token,
139+
certificateAuthorityData:
140+
cluster.currentContext.cluster.cluster[
141+
'certificate-authority-data'
142+
],
143+
});
144+
} else {
145+
const displayRetry = response.data.error !== null || allTasksError;
146+
removeLastMessage();
147+
handleError(
148+
{
149+
type: ErrorType.FATAL,
150+
message: response.data.answer.content,
151+
},
152+
displayRetry,
153+
);
154+
return;
155+
}
142156
} else if (isFeedback) {
143157
setLoading(false);
144158
} else {
@@ -185,7 +199,6 @@ export const Chat = ({
185199
setLoading(false);
186200
if (errResponse.maxAttempts === 1) {
187201
updateLatestMessage({
188-
author: Author.AI,
189202
messageChunks: [
190203
{
191204
data: {
@@ -215,7 +228,6 @@ export const Chat = ({
215228
});
216229
setLoading(true);
217230
updateLatestMessage({
218-
author: Author.AI,
219231
messageChunks: [
220232
{
221233
data: {
@@ -234,7 +246,7 @@ export const Chat = ({
234246
};
235247

236248
const retryPreviousPrompt = () => {
237-
const previousPrompt = chatGroupHelpers.findLastUserPrompt(chatHistory);
249+
const previousPrompt = chatHelpers.findLastUserPrompt(chatHistory);
238250
if (previousPrompt) {
239251
removeLastMessage();
240252
sendPrompt(previousPrompt);
@@ -311,7 +323,7 @@ export const Chat = ({
311323
// Update the context of the first group
312324
const currentContext = getCurrentContext();
313325
setChatHistory(prevGroups =>
314-
chatGroupHelpers.updateFirstGroupContext(prevGroups, currentContext),
326+
chatHelpers.updateFirstGroupContext(prevGroups, currentContext),
315327
);
316328
updateLatestMessage({
317329
messageChunks: [
@@ -390,7 +402,8 @@ export const Chat = ({
390402
author={message.author}
391403
messageChunks={message.messageChunks}
392404
isLoading={message.isLoading}
393-
hasError={message.hasError ?? false}
405+
partialAIFailure={message.partialAIFailure}
406+
hasError={message.hasError}
394407
isLatestMessage={isLastMessage}
395408
/>
396409
{isLastMessage && !message.isLoading && (
@@ -409,7 +422,7 @@ export const Chat = ({
409422
key={`${groupIndex}-${messageIndex}`}
410423
messageChunks={message.messageChunks}
411424
isLoading={message.isLoading}
412-
hasError={message.hasError ?? false}
425+
hasError={message.hasError}
413426
isLatestMessage={isLastMessage}
414427
/>
415428
);

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ interface MessageProps {
1212
author: Author;
1313
messageChunks: MessageChunk[];
1414
isLoading: boolean;
15-
hasError: boolean;
15+
partialAIFailure?: boolean;
16+
hasError?: boolean;
1617
isLatestMessage: boolean;
1718
disableFormatting?: boolean;
1819
}
@@ -21,7 +22,8 @@ export default function Message({
2122
author,
2223
messageChunks,
2324
isLoading,
24-
hasError,
25+
partialAIFailure = false,
26+
hasError = false,
2527
isLatestMessage,
2628
}: MessageProps): JSX.Element {
2729
const currentTheme = useRecoilValue(themeState);
@@ -53,6 +55,14 @@ export default function Message({
5355
>
5456
{segmentedText}
5557
</div>
58+
{partialAIFailure && (
59+
<div className={'message-error ' + className}>
60+
<Text className="error-text">
61+
{t('kyma-companion.error.partial-ai-failure')}
62+
</Text>
63+
<Icon name="error" design="Negative" className="error-icon" />
64+
</div>
65+
)}
5666
{displayError && (
5767
<div className={'message-error ' + className}>
5868
<Text className="error-text">

0 commit comments

Comments
 (0)