Browse Source

improve error handling

Shun Miyazawa 1 year ago
parent
commit
7abf162af2

+ 10 - 1
apps/app/src/features/openai/chat/components/AiChatModal/AiChatModal.tsx

@@ -12,7 +12,7 @@ import { toastError } from '~/client/util/toastr';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 import { useRagSearchModal } from '../../../client/stores/rag-search';
 import { useRagSearchModal } from '../../../client/stores/rag-search';
-import { MessageErrorCode } from '../../../interfaces/message-error';
+import { MessageErrorCode, StreamErrorCode } from '../../../interfaces/message-error';
 
 
 import { MessageCard } from './MessageCard';
 import { MessageCard } from './MessageCard';
 import { ResizableTextarea } from './ResizableTextArea';
 import { ResizableTextarea } from './ResizableTextArea';
@@ -141,6 +141,15 @@ const AiChatModalSubstance = (): JSX.Element => {
 
 
         const chunk = decoder.decode(value);
         const chunk = decoder.decode(value);
 
 
+        if (chunk.startsWith('error:')) {
+          const error = JSON.parse(chunk.replace('error: ', ''));
+          logger.error(error.errorMessage);
+          form.setError('input', { type: 'manual', message: error.message });
+          if (error.code === StreamErrorCode.RATE_LIMIT_EXCEEDED) {
+            toastError(t('API の利用条件に達しました'));
+          }
+        }
+
         // Extract text values from the chunk
         // Extract text values from the chunk
         const textValues = chunk
         const textValues = chunk
           .split('\n\n')
           .split('\n\n')

+ 6 - 0
apps/app/src/features/openai/interfaces/message-error.ts

@@ -1,3 +1,9 @@
 export const MessageErrorCode = {
 export const MessageErrorCode = {
   THREAD_ID_IS_NOT_SET: 'thread-id-is-not-set',
   THREAD_ID_IS_NOT_SET: 'thread-id-is-not-set',
 } as const;
 } as const;
+
+export const StreamErrorCode = {
+  RATE_LIMIT_EXCEEDED: 'rate_limit_exceeded',
+} as const;
+
+export type StreamErrorCode = typeof StreamErrorCode[keyof typeof StreamErrorCode];

+ 13 - 1
apps/app/src/features/openai/server/routes/message.ts

@@ -11,7 +11,7 @@ import { apiV3FormValidator } from '~/server/middlewares/apiv3-form-validator';
 import type { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';
 import type { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
-import { MessageErrorCode } from '../../interfaces/message-error';
+import { MessageErrorCode, StreamErrorCode } from '../../interfaces/message-error';
 import { openaiClient } from '../services';
 import { openaiClient } from '../services';
 
 
 import { certifyAiService } from './middlewares/certify-ai-service';
 import { certifyAiService } from './middlewares/certify-ai-service';
@@ -80,6 +80,18 @@ export const postMessageHandlersFactory: PostMessageHandlersFactory = (crowi) =>
         res.write(`data: ${JSON.stringify(delta)}\n\n`);
         res.write(`data: ${JSON.stringify(delta)}\n\n`);
       };
       };
 
 
+      const sendError = (code: StreamErrorCode, message: string) => {
+        res.write(`error: ${JSON.stringify({ code, message })}`);
+      };
+
+      stream.on('event', (delta) => {
+        if (delta.event === 'thread.run.failed') {
+          if (delta.data.last_error?.code === StreamErrorCode.RATE_LIMIT_EXCEEDED) {
+            logger.error(delta.data.last_error.message);
+            sendError(StreamErrorCode.RATE_LIMIT_EXCEEDED, delta.data.last_error.message);
+          }
+        }
+      });
       stream.on('messageDelta', messageDeltaHandler);
       stream.on('messageDelta', messageDeltaHandler);
       stream.once('messageDone', () => {
       stream.once('messageDone', () => {
         stream.off('messageDelta', messageDeltaHandler);
         stream.off('messageDelta', messageDeltaHandler);