Explorar o código

Improve error handling

Shun Miyazawa hai 1 ano
pai
achega
e58729faf6

+ 2 - 1
apps/app/public/static/locales/en_US/translation.json

@@ -490,7 +490,8 @@
     "title_beta_label": "(Beta)",
     "placeholder": "Ask me anything.",
     "caution_against_hallucination": "Please verify the information and check the sources.",
-    "progress_label": "Generating answers"
+    "progress_label": "Generating answers",
+    "thread_id_is_not_set": "Failed to generate thread"
   },
   "link_edit": {
     "edit_link": "Edit Link",

+ 2 - 1
apps/app/public/static/locales/fr_FR/translation.json

@@ -484,7 +484,8 @@
     "title_beta_label": "(Bêta)",
     "placeholder": "Demandez-moi n'importe quoi.",
     "caution_against_hallucination": "Veuillez vérifier les informations et consulter les sources.",
-    "progress_label": "Génération des réponses"
+    "progress_label": "Génération des réponses",
+    "thread_id_is_not_set": "Échec de la génération d'un fil de discussion"
   },
   "link_edit": {
     "edit_link": "Modifier lien",

+ 2 - 1
apps/app/public/static/locales/ja_JP/translation.json

@@ -523,7 +523,8 @@
     "title_beta_label": "(ベータ)",
     "placeholder": "ききたいことを入力してください",
     "caution_against_hallucination": "情報が正しいか出典を確認しましょう",
-    "progress_label": "回答を生成しています"
+    "progress_label": "回答を生成しています",
+    "thread_id_is_not_set": "スレッドの生成に失敗しました"
   },
   "link_edit": {
     "edit_link": "リンク編集",

+ 2 - 1
apps/app/public/static/locales/zh_CN/translation.json

@@ -479,7 +479,8 @@
     "title_beta_label": "(测试版)",
     "placeholder": "问我任何问题。",
     "caution_against_hallucination": "请核实信息并检查来源。",
-    "progress_label": "生成答案中"
+    "progress_label": "生成答案中",
+    "thread_id_is_not_set": "生成线程失败"
   },
   "link_edit": {
     "edit_link": "Edit Link",

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

@@ -8,9 +8,11 @@ import {
 } from 'reactstrap';
 
 import { apiv3Post } from '~/client/util/apiv3-client';
+import { toastError } from '~/client/util/toastr';
 import loggerFactory from '~/utils/logger';
 
 import { useRagSearchModal } from '../../../client/stores/rag-search';
+import { MessageErrorCode } from '../../../interfaces/message-error';
 
 import { MessageCard } from './MessageCard';
 import { ResizableTextarea } from './ResizableTextArea';
@@ -108,6 +110,11 @@ const AiChatModalSubstance = (): JSX.Element => {
           // eslint-disable-next-line @typescript-eslint/no-unused-vars
           const errors = resJson.errors.map(({ message }) => message).join(', ');
           form.setError('input', { type: 'manual', message: `[${response.status}] ${errors}` });
+
+          const hasThreadIdNotSet = resJson.errors.some(err => err.code === MessageErrorCode.THREAD_ID_IS_NOT_SET);
+          if (hasThreadIdNotSet) {
+            toastError(t('modal_aichat.thread_id_is_not_set'));
+          }
         }
         setGeneratingAnswerMessage(undefined);
         return;
@@ -160,7 +167,7 @@ const AiChatModalSubstance = (): JSX.Element => {
       form.setError('input', { type: 'manual', message: err.toString() });
     }
 
-  }, [form, isGenerating, messageLogs, threadId]);
+  }, [form, isGenerating, messageLogs, t, threadId]);
 
   const keyDownHandler = (event: KeyboardEvent<HTMLTextAreaElement>) => {
     if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {

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

@@ -0,0 +1,3 @@
+export const MessageErrorCode = {
+  THREAD_ID_IS_NOT_SET: 'thread-id-is-not-set',
+} as const;

+ 8 - 5
apps/app/src/features/openai/server/routes/message.ts

@@ -1,5 +1,4 @@
-import assert from 'assert';
-
+import { ErrorV3 } from '@growi/core/dist/models';
 import type { Request, RequestHandler, Response } from 'express';
 import type { ValidationChain } from 'express-validator';
 import { body } from 'express-validator';
@@ -9,8 +8,10 @@ import type { MessageDelta } from 'openai/resources/beta/threads/messages.mjs';
 import { getOrCreateChatAssistant } from '~/features/openai/server/services/assistant';
 import type Crowi from '~/server/crowi';
 import { apiV3FormValidator } from '~/server/middlewares/apiv3-form-validator';
+import type { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';
 import loggerFactory from '~/utils/logger';
 
+import { MessageErrorCode } from '../../interfaces/message-error';
 import { openaiClient } from '../services';
 
 import { certifyAiService } from './middlewares/certify-ai-service';
@@ -37,16 +38,18 @@ export const postMessageHandlersFactory: PostMessageHandlersFactory = (crowi) =>
       .withMessage('userMessage must be string')
       .notEmpty()
       .withMessage('userMessage must be set'),
-    body('threadId').isString().withMessage('threadId must be string'),
+    body('threadId').optional().isString().withMessage('threadId must be string'),
   ];
 
   return [
     accessTokenParser, loginRequiredStrictly, certifyAiService, validator, apiV3FormValidator,
-    async(req: Req, res: Response) => {
+    async(req: Req, res: ApiV3Response) => {
 
       const threadId = req.body.threadId;
 
-      assert(threadId != null);
+      if (threadId == null) {
+        return res.apiv3Err(new ErrorV3('threadId is not set', MessageErrorCode.THREAD_ID_IS_NOT_SET), 400);
+      }
 
       let stream: AssistantStream;