Browse Source

Merge branch 'Yidadaa:main' into main

Gan-Xing 1 year ago
parent
commit
8568fc3544
5 changed files with 47 additions and 42 deletions
  1. 2 3
      app/api/auth.ts
  2. 38 36
      app/client/platforms/openai.ts
  3. 1 3
      app/store/chat.ts
  4. 1 0
      package.json
  5. 5 0
      yarn.lock

+ 2 - 3
app/api/auth.ts

@@ -43,8 +43,7 @@ export function auth(req: NextRequest) {
   if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) {
     return {
       error: true,
-      needAccessCode: true,
-      msg: "Please go settings page and fill your access code.",
+      msg: !accessCode ? "empty access code" : "wrong access code",
     };
   }
 
@@ -58,7 +57,7 @@ export function auth(req: NextRequest) {
       console.log("[Auth] admin did not provide an api key");
       return {
         error: true,
-        msg: "Empty Api Key",
+        msg: "admin did not provide an api key",
       };
     }
   } else {

+ 38 - 36
app/client/platforms/openai.ts

@@ -3,6 +3,8 @@ import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
 
 import { ChatOptions, getHeaders, LLMApi, LLMUsage } from "../api";
 import Locale from "../../locales";
+import { fetchEventSource } from "@microsoft/fetch-event-source";
+import { prettyObject } from "@/app/utils/format";
 
 export class ChatGPTApi implements LLMApi {
   public ChatPath = "v1/chat/completions";
@@ -10,8 +12,10 @@ export class ChatGPTApi implements LLMApi {
   public SubsPath = "dashboard/billing/subscription";
 
   path(path: string): string {
-    const openaiUrl = useAccessStore.getState().openaiUrl;
-    if (openaiUrl.endsWith("/")) openaiUrl.slice(0, openaiUrl.length - 1);
+    let openaiUrl = useAccessStore.getState().openaiUrl;
+    if (openaiUrl.endsWith("/")) {
+      openaiUrl = openaiUrl.slice(0, openaiUrl.length - 1);
+    }
     return [openaiUrl, path].join("/");
   }
 
@@ -69,40 +73,32 @@ export class ChatGPTApi implements LLMApi {
           options.onFinish(responseText);
         };
 
-        const res = await fetch(chatPath, chatPayload);
-        clearTimeout(reqestTimeoutId);
+        controller.signal.onabort = finish;
+
+        fetchEventSource(chatPath, {
+          ...chatPayload,
+          async onopen(res) {
+            clearTimeout(reqestTimeoutId);
+            if (res.status === 401) {
+              let extraInfo = { error: undefined };
+              try {
+                extraInfo = await res.clone().json();
+              } catch {}
+
+              responseText += "\n\n" + Locale.Error.Unauthorized;
 
-        if (res.status === 401) {
-          responseText += "\n\n" + Locale.Error.Unauthorized;
-          return finish();
-        }
-
-        if (
-          !res.ok ||
-          !res.headers.get("Content-Type")?.includes("stream") ||
-          !res.body
-        ) {
-          return options.onError?.(new Error());
-        }
-
-        const reader = res.body.getReader();
-        const decoder = new TextDecoder("utf-8");
-
-        while (true) {
-          const { done, value } = await reader.read();
-          if (done) {
-            return finish();
-          }
-
-          const chunk = decoder.decode(value, { stream: true });
-          const lines = chunk.split("data: ");
-
-          for (const line of lines) {
-            const text = line.trim();
-            if (line.startsWith("[DONE]")) {
+              if (extraInfo.error) {
+                responseText += "\n\n" + prettyObject(extraInfo);
+              }
+
+              return finish();
+            }
+          },
+          onmessage(msg) {
+            if (msg.data === "[DONE]") {
               return finish();
             }
-            if (text.length === 0) continue;
+            const text = msg.data;
             try {
               const json = JSON.parse(text);
               const delta = json.choices[0].delta.content;
@@ -111,10 +107,16 @@ export class ChatGPTApi implements LLMApi {
                 options.onUpdate?.(responseText, delta);
               }
             } catch (e) {
-              console.error("[Request] parse error", text, chunk);
+              console.error("[Request] parse error", text, msg);
             }
-          }
-        }
+          },
+          onclose() {
+            finish();
+          },
+          onerror(e) {
+            options.onError?.(e);
+          },
+        });
       } else {
         const res = await fetch(chatPath, chatPayload);
         clearTimeout(reqestTimeoutId);

+ 1 - 3
app/store/chat.ts

@@ -296,9 +296,7 @@ export const useChatStore = create<ChatStore>()(
               botMessage.content !== Locale.Error.Unauthorized &&
               !isAborted
             ) {
-              botMessage.content += "\n\n" + Locale.Store.Error;
-            } else if (botMessage.content.length === 0) {
-              botMessage.content = prettyObject(error);
+              botMessage.content += "\n\n" + prettyObject(error);
             }
             botMessage.streaming = false;
             userMessage.isError = !isAborted;

+ 1 - 0
package.json

@@ -14,6 +14,7 @@
   },
   "dependencies": {
     "@hello-pangea/dnd": "^16.2.0",
+    "@microsoft/fetch-event-source": "^2.0.1",
     "@svgr/webpack": "^6.5.1",
     "@vercel/analytics": "^0.1.11",
     "emoji-picker-react": "^4.4.7",

+ 5 - 0
yarn.lock

@@ -1111,6 +1111,11 @@
   dependencies:
     "@types/react" ">=16.0.0"
 
+"@microsoft/fetch-event-source@^2.0.1":
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz#9ceecc94b49fbaa15666e38ae8587f64acce007d"
+  integrity sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==
+
 "@next/env@13.4.2":
   version "13.4.2"
   resolved "https://registry.npmmirror.com/@next/env/-/env-13.4.2.tgz#cf3ebfd523a33d8404c1216e02ac8d856a73170e"