Skip to content

Commit a32d900

Browse files
authored
Merge pull request #3405 from RedisInsight/fe/feature/RI-5758
#RI-5758 - handle limit errors
2 parents 924ad60 + 0386cff commit a32d900

File tree

5 files changed

+97
-27
lines changed

5 files changed

+97
-27
lines changed

redisinsight/ui/src/components/side-panels/panels/ai-assistant/components/shared/error-message/ErrorMessage.spec.tsx

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import React from 'react'
22
import { render, screen } from 'uiSrc/utils/test-utils'
33

4-
import { AiChatErrors } from 'uiSrc/constants/apiErrors'
4+
import { AI_CHAT_ERRORS } from 'uiSrc/constants/apiErrors'
5+
import { CustomErrorCodes } from 'uiSrc/constants'
56
import ErrorMessage from './ErrorMessage'
67

78
describe('ErrorMessage', () => {
@@ -13,17 +14,46 @@ describe('ErrorMessage', () => {
1314
const onRestart = jest.fn()
1415
render(<ErrorMessage onRestart={onRestart} error={{ statusCode: 404 }} />)
1516

16-
expect(screen.getByTestId('ai-chat-error-message')).toHaveTextContent(AiChatErrors.Default)
17+
expect(screen.getByTestId('ai-chat-error-message')).toHaveTextContent(AI_CHAT_ERRORS.default())
1718
expect(screen.getByTestId('ai-chat-error-report-link')).toBeInTheDocument()
1819

1920
expect(screen.getByTestId('ai-chat-error-restart-session-btn')).toBeInTheDocument()
21+
expect(screen.getByTestId('ai-chat-error-report-link')).toBeInTheDocument()
22+
})
23+
24+
it('should render rate limit error', () => {
25+
const error = {
26+
errorCode: CustomErrorCodes.AiQueryRateLimitRequest,
27+
statusCode: 429,
28+
details: { limiterType: 'request', limiterKind: 'user', limiterSeconds: 100 }
29+
}
30+
render(<ErrorMessage onRestart={jest.fn} error={error} />)
31+
32+
expect(screen.getByTestId('ai-chat-error-message')).toHaveTextContent(AI_CHAT_ERRORS.rateLimit(100))
33+
34+
expect(screen.queryByTestId('ai-chat-error-restart-session-btn')).not.toBeInTheDocument()
35+
expect(screen.queryByTestId('ai-chat-error-report-link')).not.toBeInTheDocument()
36+
})
37+
38+
it('should render tokens limit error', () => {
39+
const error = {
40+
errorCode: CustomErrorCodes.AiQueryRateLimitMaxTokens,
41+
statusCode: 413,
42+
details: { tokenLimit: 20000, tokenCount: 575 }
43+
}
44+
render(<ErrorMessage onRestart={jest.fn} error={error} />)
45+
46+
expect(screen.getByTestId('ai-chat-error-message')).toHaveTextContent(AI_CHAT_ERRORS.tokenLimit())
47+
48+
expect(screen.getByTestId('ai-chat-error-restart-session-btn')).toBeInTheDocument()
49+
expect(screen.queryByTestId('ai-chat-error-report-link')).not.toBeInTheDocument()
2050
})
2151

2252
it('should not render restart button with timeout error', () => {
2353
const onRestart = jest.fn()
2454
render(<ErrorMessage onRestart={onRestart} error={{ statusCode: 408 }} />)
2555

26-
expect(screen.getByTestId('ai-chat-error-message')).toHaveTextContent(AiChatErrors.Timeout)
56+
expect(screen.getByTestId('ai-chat-error-message')).toHaveTextContent(AI_CHAT_ERRORS.timeout())
2757
expect(screen.getByTestId('ai-chat-error-report-link')).toBeInTheDocument()
2858

2959
expect(screen.queryByTestId('ai-chat-error-restart-session-btn')).not.toBeInTheDocument()

redisinsight/ui/src/components/side-panels/panels/ai-assistant/components/shared/error-message/ErrorMessage.tsx

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,48 +3,80 @@ import { EuiButton } from '@elastic/eui'
33

44
import { EXTERNAL_LINKS } from 'uiSrc/constants/links'
55
import { CustomErrorCodes } from 'uiSrc/constants'
6-
import { AiChatErrors } from 'uiSrc/constants/apiErrors'
6+
import { AI_CHAT_ERRORS } from 'uiSrc/constants/apiErrors'
77
import ApiStatusCode from 'uiSrc/constants/apiStatusCode'
88

99
import RestartChat from '../restart-chat'
1010

1111
import styles from './styles.module.scss'
1212

1313
export interface Props {
14-
error?: { statusCode: number, errorCode?: number }
14+
error?: {
15+
statusCode: number
16+
errorCode?: number
17+
details?: Record<string, any>
18+
}
1519
onRestart: () => void
1620
}
1721

22+
const ERROR_CODES_WITHOUT_RESTART = [
23+
CustomErrorCodes.CloudApiUnauthorized,
24+
CustomErrorCodes.GeneralAiUnexpectedError,
25+
CustomErrorCodes.AiQueryRateLimitRequest,
26+
CustomErrorCodes.AiQueryRateLimitToken,
27+
]
28+
29+
const ERROR_CODES_WITHOUT_REPORT_ISSUE = [
30+
CustomErrorCodes.AiQueryRateLimitRequest,
31+
CustomErrorCodes.AiQueryRateLimitToken,
32+
CustomErrorCodes.AiQueryRateLimitMaxTokens,
33+
]
34+
1835
const ErrorMessage = (props: Props) => {
1936
const { error, onRestart } = props
2037

21-
const getErrorMessage = (error?: { statusCode: number, errorCode?: number }): string => {
22-
if (error?.statusCode === ApiStatusCode.Timeout) return AiChatErrors.Timeout
23-
if (error?.errorCode === CustomErrorCodes.GeneralAiUnexpectedError) return AiChatErrors.DefaultUnexpected
38+
const getErrorMessage = (
39+
error?: {
40+
statusCode: number,
41+
errorCode?: number,
42+
details?: Record<string, any> }
43+
): string => {
44+
const { statusCode, errorCode, details } = error || {}
2445

25-
return AiChatErrors.Default
46+
if (statusCode === ApiStatusCode.Timeout) return AI_CHAT_ERRORS.timeout()
47+
if (errorCode === CustomErrorCodes.GeneralAiUnexpectedError) return AI_CHAT_ERRORS.unexpected()
48+
if (errorCode === CustomErrorCodes.AiQueryRateLimitRequest
49+
|| errorCode === CustomErrorCodes.AiQueryRateLimitToken
50+
) return AI_CHAT_ERRORS.rateLimit(details?.limiterSeconds)
51+
if (errorCode === CustomErrorCodes.AiQueryRateLimitMaxTokens) return AI_CHAT_ERRORS.tokenLimit()
52+
53+
return AI_CHAT_ERRORS.default()
2654
}
2755

2856
if (!error) return null
2957

30-
const isShowRestart = error.errorCode !== CustomErrorCodes.CloudApiUnauthorized
31-
&& error.errorCode !== CustomErrorCodes.GeneralAiUnexpectedError
58+
const isShowRestart = !(error.errorCode && ERROR_CODES_WITHOUT_RESTART.includes(error.errorCode))
3259
&& error.statusCode !== ApiStatusCode.Timeout
60+
const isShowReportIssue = !(error.errorCode && ERROR_CODES_WITHOUT_REPORT_ISSUE.includes(error.errorCode))
3361

3462
return (
3563
<>
3664
<div className={styles.errorMessage} data-testid="ai-chat-error-message">
3765
{getErrorMessage(error)}
38-
{' '}
39-
<a
40-
className="link-underline"
41-
href={EXTERNAL_LINKS.githubIssues}
42-
data-testid="ai-chat-error-report-link"
43-
target="_blank"
44-
rel="noreferrer"
45-
>
46-
report the issue
47-
</a>
66+
{isShowReportIssue && (
67+
<>
68+
{' '}
69+
<a
70+
className="link-underline"
71+
href={EXTERNAL_LINKS.githubIssues}
72+
data-testid="ai-chat-error-report-link"
73+
target="_blank"
74+
rel="noreferrer"
75+
>
76+
report the issue
77+
</a>
78+
</>
79+
)}
4880
</div>
4981
{isShowRestart && (
5082
<RestartChat

redisinsight/ui/src/constants/apiErrors.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ export const ApiEncryptionErrors: string[] = [
1313
ApiErrors.KeytarDecryption,
1414
]
1515

16-
export enum AiChatErrors {
17-
Default = 'An error occurred. Try again or restart the session.',
18-
DefaultUnexpected = 'An unexpected error occurred. Try again later.',
19-
Timeout = 'Timeout occurred. Try again later.',
20-
CloudAuthorization = 'Session expired. Login and try again'
16+
export const AI_CHAT_ERRORS = {
17+
default: () => 'An error occurred. Try again or restart the session.',
18+
unexpected: () => 'An unexpected error occurred. Try again later.',
19+
timeout: () => 'Timeout occurred. Try again later.',
20+
rateLimit: (limit = 5000) => `Exceeded rate limit. Try again in ${limit} seconds.`,
21+
tokenLimit: () => 'Conversation is too long. Restart the session.'
2122
}
2223

2324
export default ApiErrors

redisinsight/ui/src/constants/customErrorCodes.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,9 @@ export enum CustomErrorCodes {
4848
QueryAiBadRequest = 11_353,
4949
QueryAiNotFound = 11_354,
5050

51+
AiQueryRateLimitRequest = 11_360,
52+
AiQueryRateLimitToken = 11_361,
53+
AiQueryRateLimitMaxTokens = 11362,
54+
5155
GeneralAiUnexpectedError = 11_391
5256
}

redisinsight/ui/src/utils/api/chatbots.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,11 @@ export const getStreamedAnswer = async (
3535
const reader = response.body!.pipeThrough(new TextDecoderStream()).getReader()
3636
if (!isStatusSuccessful(response.status)) {
3737
const { value } = await reader.read()
38+
39+
const errorResponse = value ? JSON.parse(value) : {}
3840
const extendedResponseError = {
39-
errorCode: value ? JSON.parse(value).errorCode : ''
41+
errorCode: errorResponse.errorCode ?? '',
42+
details: errorResponse.details ?? {}
4043
}
4144
const error = Object.assign(response, extendedResponseError)
4245
onError?.(error)

0 commit comments

Comments
 (0)