Skip to content

Commit 003d25e

Browse files
committed
#RI-5758 - handle limit errors
1 parent 25d513c commit 003d25e

File tree

5 files changed

+73
-17
lines changed

5 files changed

+73
-17
lines changed

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

Lines changed: 30 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,43 @@ 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()
2021
})
2122

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

26-
expect(screen.getByTestId('ai-chat-error-message')).toHaveTextContent(AiChatErrors.Timeout)
53+
expect(screen.getByTestId('ai-chat-error-message')).toHaveTextContent(AI_CHAT_ERRORS.timeout())
2754
expect(screen.getByTestId('ai-chat-error-report-link')).toBeInTheDocument()
2855

2956
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: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,53 @@ 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+
1829
const ErrorMessage = (props: Props) => {
1930
const { error, onRestart } = props
2031

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
32+
const getErrorMessage = (
33+
error?: {
34+
statusCode: number,
35+
errorCode?: number,
36+
details?: Record<string, any> }
37+
): string => {
38+
const { statusCode, errorCode, details } = error || {}
39+
40+
if (statusCode === ApiStatusCode.Timeout) return AI_CHAT_ERRORS.timeout()
41+
if (errorCode === CustomErrorCodes.GeneralAiUnexpectedError) return AI_CHAT_ERRORS.unexpected()
42+
if (errorCode === CustomErrorCodes.AiQueryRateLimitRequest
43+
|| errorCode === CustomErrorCodes.AiQueryRateLimitToken
44+
) return AI_CHAT_ERRORS.rateLimit(details?.limiterSeconds)
45+
if (errorCode === CustomErrorCodes.AiQueryRateLimitMaxTokens) return AI_CHAT_ERRORS.tokenLimit()
2446

25-
return AiChatErrors.Default
47+
return AI_CHAT_ERRORS.default()
2648
}
2749

2850
if (!error) return null
2951

30-
const isShowRestart = error.errorCode !== CustomErrorCodes.CloudApiUnauthorized
31-
&& error.errorCode !== CustomErrorCodes.GeneralAiUnexpectedError
52+
const isShowRestart = !(error.errorCode && ERROR_CODES_WITHOUT_RESTART.includes(error.errorCode))
3253
&& error.statusCode !== ApiStatusCode.Timeout
3354

3455
return (

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)