ソースを参照

Merge branch 'main' into chatGPT

Kivi1998 1 年間 前
コミット
7f3516f44f
12 ファイル変更108 行追加66 行削除
  1. 5 1
      README.md
  2. 3 0
      README_CN.md
  3. 2 1
      app/components/chat.tsx
  4. 2 0
      app/config/build.ts
  5. 3 0
      app/config/server.ts
  6. 15 15
      app/store/chat.ts
  7. 11 3
      app/store/config.ts
  8. 0 42
      app/utils.ts
  9. 54 0
      app/utils/chat.ts
  10. 7 4
      app/utils/cloud/upstash.ts
  11. 1 0
      package.json
  12. 5 0
      yarn.lock

+ 5 - 1
README.md

@@ -245,13 +245,17 @@ To control custom models, use `+` to add a custom model, use `-` to hide a model
 
 User `-all` to disable all default models, `+all` to enable all default models.
 
-### `WHITE_WEBDEV_ENDPOINTS` (可选)
+### `WHITE_WEBDEV_ENDPOINTS` (optional)
 
 You can use this option if you want to increase the number of webdav service addresses you are allowed to access, as required by the format:
 - Each address must be a complete endpoint 
 > `https://xxxx/yyy`
 - Multiple addresses are connected by ', '
 
+### `DEFAULT_INPUT_TEMPLATE` (optional)
+
+Customize the default template used to initialize the User Input Preprocessing configuration item in Settings.
+
 ## Requirements
 
 NodeJS >= 18, Docker >= 20

+ 3 - 0
README_CN.md

@@ -156,6 +156,9 @@ anthropic claude Api Url.
 
 用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。
 
+### `DEFAULT_INPUT_TEMPLATE` (可选)
+自定义默认的 template,用于初始化『设置』中的『用户输入预处理』配置项
+
 ## 开发
 
 点击下方按钮,开始二次开发:

+ 2 - 1
app/components/chat.tsx

@@ -59,9 +59,10 @@ import {
   getMessageTextContent,
   getMessageImages,
   isVisionModel,
-  compressImage,
 } from "../utils";
 
+import { compressImage } from "@/app/utils/chat";
+
 import dynamic from "next/dynamic";
 
 import { ChatControllerPool } from "../client/controller";

+ 2 - 0
app/config/build.ts

@@ -1,4 +1,5 @@
 import tauriConfig from "../../src-tauri/tauri.conf.json";
+import { DEFAULT_INPUT_TEMPLATE } from "../constant";
 
 export const getBuildConfig = () => {
   if (typeof process === "undefined") {
@@ -38,6 +39,7 @@ export const getBuildConfig = () => {
     ...commitInfo,
     buildMode,
     isApp,
+    template: process.env.DEFAULT_INPUT_TEMPLATE ?? DEFAULT_INPUT_TEMPLATE,
   };
 };
 

+ 3 - 0
app/config/server.ts

@@ -34,6 +34,9 @@ declare global {
 
       // google tag manager
       GTM_ID?: string;
+
+      // custom template for preprocessing user input
+      DEFAULT_INPUT_TEMPLATE?: string;
     }
   }
 }

+ 15 - 15
app/store/chat.ts

@@ -428,14 +428,13 @@ export const useChatStore = createPersistStore(
       getMemoryPrompt() {
         const session = get().currentSession();
 
-        return {
-          role: "system",
-          content:
-            session.memoryPrompt.length > 0
-              ? Locale.Store.Prompt.History(session.memoryPrompt)
-              : "",
-          date: "",
-        } as ChatMessage;
+        if (session.memoryPrompt.length) {
+          return {
+            role: "system",
+            content: Locale.Store.Prompt.History(session.memoryPrompt),
+            date: "",
+          } as ChatMessage;
+        }
       },
 
       getMessagesWithMemory() {
@@ -471,16 +470,15 @@ export const useChatStore = createPersistStore(
             systemPrompts.at(0)?.content ?? "empty",
           );
         }
-
+        const memoryPrompt = get().getMemoryPrompt();
         // long term memory
         const shouldSendLongTermMemory =
           modelConfig.sendMemory &&
           session.memoryPrompt &&
           session.memoryPrompt.length > 0 &&
           session.lastSummarizeIndex > clearContextIndex;
-        const longTermMemoryPrompts = shouldSendLongTermMemory
-          ? [get().getMemoryPrompt()]
-          : [];
+        const longTermMemoryPrompts =
+          shouldSendLongTermMemory && memoryPrompt ? [memoryPrompt] : [];
         const longTermMemoryStartIndex = session.lastSummarizeIndex;
 
         // short term memory
@@ -605,9 +603,11 @@ export const useChatStore = createPersistStore(
             Math.max(0, n - modelConfig.historyMessageCount),
           );
         }
-
-        // add memory prompt
-        toBeSummarizedMsgs.unshift(get().getMemoryPrompt());
+        const memoryPrompt = get().getMemoryPrompt();
+        if (memoryPrompt) {
+          // add memory prompt
+          toBeSummarizedMsgs.unshift(memoryPrompt);
+        }
 
         const lastSummarizeIndex = session.messages.length;
 

+ 11 - 3
app/store/config.ts

@@ -1,5 +1,4 @@
 import { LLMModel } from "../client/api";
-import { isMacOS } from "../utils";
 import { getClientConfig } from "../config/client";
 import {
   DEFAULT_INPUT_TEMPLATE,
@@ -25,6 +24,8 @@ export enum Theme {
   Light = "light",
 }
 
+const config = getClientConfig();
+
 export const DEFAULT_CONFIG = {
   lastUpdate: Date.now(), // timestamp, to merge state
 
@@ -56,7 +57,7 @@ export const DEFAULT_CONFIG = {
     historyMessageCount: 4,
     compressMessageLengthThreshold: 1000,
     enableInjectSystemPrompts: true,
-    template: DEFAULT_INPUT_TEMPLATE,
+    template: config?.template ?? DEFAULT_INPUT_TEMPLATE,
   },
 };
 
@@ -132,7 +133,7 @@ export const useAppConfig = createPersistStore(
   }),
   {
     name: StoreKey.Config,
-    version: 3.8,
+    version: 3.9,
     migrate(persistedState, version) {
       const state = persistedState as ChatConfig;
 
@@ -163,6 +164,13 @@ export const useAppConfig = createPersistStore(
         state.lastUpdate = Date.now();
       }
 
+      if (version < 3.9) {
+        state.modelConfig.template =
+          state.modelConfig.template !== DEFAULT_INPUT_TEMPLATE
+            ? state.modelConfig.template
+            : config?.template ?? DEFAULT_INPUT_TEMPLATE;
+      }
+
       return state as any;
     },
   },

+ 0 - 42
app/utils.ts

@@ -83,48 +83,6 @@ export async function downloadAs(text: string, filename: string) {
   }
 }
 
-export function compressImage(file: File, maxSize: number): Promise<string> {
-  return new Promise((resolve, reject) => {
-    const reader = new FileReader();
-    reader.onload = (readerEvent: any) => {
-      const image = new Image();
-      image.onload = () => {
-        let canvas = document.createElement("canvas");
-        let ctx = canvas.getContext("2d");
-        let width = image.width;
-        let height = image.height;
-        let quality = 0.9;
-        let dataUrl;
-
-        do {
-          canvas.width = width;
-          canvas.height = height;
-          ctx?.clearRect(0, 0, canvas.width, canvas.height);
-          ctx?.drawImage(image, 0, 0, width, height);
-          dataUrl = canvas.toDataURL("image/jpeg", quality);
-
-          if (dataUrl.length < maxSize) break;
-
-          if (quality > 0.5) {
-            // Prioritize quality reduction
-            quality -= 0.1;
-          } else {
-            // Then reduce the size
-            width *= 0.9;
-            height *= 0.9;
-          }
-        } while (dataUrl.length > maxSize);
-
-        resolve(dataUrl);
-      };
-      image.onerror = reject;
-      image.src = readerEvent.target.result;
-    };
-    reader.onerror = reject;
-    reader.readAsDataURL(file);
-  });
-}
-
 export function readFromFile() {
   return new Promise<string>((res, rej) => {
     const fileInput = document.createElement("input");

+ 54 - 0
app/utils/chat.ts

@@ -0,0 +1,54 @@
+import heic2any from "heic2any";
+
+export function compressImage(file: File, maxSize: number): Promise<string> {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader();
+    reader.onload = (readerEvent: any) => {
+      const image = new Image();
+      image.onload = () => {
+        let canvas = document.createElement("canvas");
+        let ctx = canvas.getContext("2d");
+        let width = image.width;
+        let height = image.height;
+        let quality = 0.9;
+        let dataUrl;
+
+        do {
+          canvas.width = width;
+          canvas.height = height;
+          ctx?.clearRect(0, 0, canvas.width, canvas.height);
+          ctx?.drawImage(image, 0, 0, width, height);
+          dataUrl = canvas.toDataURL("image/jpeg", quality);
+
+          if (dataUrl.length < maxSize) break;
+
+          if (quality > 0.5) {
+            // Prioritize quality reduction
+            quality -= 0.1;
+          } else {
+            // Then reduce the size
+            width *= 0.9;
+            height *= 0.9;
+          }
+        } while (dataUrl.length > maxSize);
+
+        resolve(dataUrl);
+      };
+      image.onerror = reject;
+      image.src = readerEvent.target.result;
+    };
+    reader.onerror = reject;
+
+    if (file.type.includes("heic")) {
+      heic2any({ blob: file, toType: "image/jpeg" })
+        .then((blob) => {
+          reader.readAsDataURL(blob as Blob);
+        })
+        .catch((e) => {
+          reject(e);
+        });
+    }
+
+    reader.readAsDataURL(file);
+  });
+}

+ 7 - 4
app/utils/cloud/upstash.ts

@@ -93,14 +93,17 @@ export function createUpstashClient(store: SyncStore) {
       }
 
       let url;
-      if (proxyUrl.length > 0 || proxyUrl === "/") {
-        let u = new URL(proxyUrl + "/api/upstash/" + path);
+      const pathPrefix = "/api/upstash/";
+
+      try {
+        let u = new URL(proxyUrl + pathPrefix + path);
         // add query params
         u.searchParams.append("endpoint", config.endpoint);
         url = u.toString();
-      } else {
-        url = "/api/upstash/" + path + "?endpoint=" + config.endpoint;
+      } catch (e) {
+        url = pathPrefix + path + "?endpoint=" + config.endpoint;
       }
+
       return url;
     },
   };

+ 1 - 0
package.json

@@ -24,6 +24,7 @@
     "@vercel/speed-insights": "^1.0.2",
     "emoji-picker-react": "^4.9.2",
     "fuse.js": "^7.0.0",
+    "heic2any": "^0.0.4",
     "html-to-image": "^1.11.11",
     "mermaid": "^10.6.1",
     "nanoid": "^5.0.3",

+ 5 - 0
yarn.lock

@@ -3669,6 +3669,11 @@ heap@^0.2.6:
   resolved "https://registry.npmmirror.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc"
   integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==
 
+heic2any@^0.0.4:
+  version "0.0.4"
+  resolved "https://registry.npmmirror.com/heic2any/-/heic2any-0.0.4.tgz#eddb8e6fec53c8583a6e18b65069bb5e8d19028a"
+  integrity sha512-3lLnZiDELfabVH87htnRolZ2iehX9zwpRyGNz22GKXIu0fznlblf0/ftppXKNqS26dqFSeqfIBhAmAj/uSp0cA==
+
 highlight.js@~11.7.0:
   version "11.7.0"
   resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.7.0.tgz#3ff0165bc843f8c9bce1fd89e2fda9143d24b11e"