Browse Source

fix: #963 config not work

Yidadaa 1 year ago
parent
commit
ae479f4a92

+ 11 - 11
app/components/chat.tsx

@@ -32,6 +32,7 @@ import {
   useAccessStore,
   Theme,
   ModelType,
+  useAppConfig,
 } from "../store";
 
 import {
@@ -69,7 +70,7 @@ const Emoji = dynamic(async () => (await import("emoji-picker-react")).Emoji, {
 });
 
 export function Avatar(props: { role: Message["role"]; model?: ModelType }) {
-  const config = useChatStore((state) => state.config);
+  const config = useAppConfig();
 
   if (props.role !== "user") {
     return (
@@ -285,7 +286,7 @@ function PromptToast(props: {
 }
 
 function useSubmitHandler() {
-  const config = useChatStore((state) => state.config);
+  const config = useAppConfig();
   const submitKey = config.submitKey;
 
   const shouldSubmit = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
@@ -361,16 +362,16 @@ export function ChatActions(props: {
   scrollToBottom: () => void;
   hitBottom: boolean;
 }) {
-  const chatStore = useChatStore();
+  const config = useAppConfig();
 
   // switch themes
-  const theme = chatStore.config.theme;
+  const theme = config.theme;
   function nextTheme() {
     const themes = [Theme.Auto, Theme.Light, Theme.Dark];
     const themeIndex = themes.indexOf(theme);
     const nextIndex = (themeIndex + 1) % themes.length;
     const nextTheme = themes[nextIndex];
-    chatStore.updateConfig((config) => (config.theme = nextTheme));
+    config.update((config) => (config.theme = nextTheme));
   }
 
   // stop all responses
@@ -428,7 +429,8 @@ export function Chat() {
     state.currentSession(),
     state.currentSessionIndex,
   ]);
-  const fontSize = useChatStore((state) => state.config.fontSize);
+  const config = useAppConfig();
+  const fontSize = config.fontSize;
 
   const inputRef = useRef<HTMLTextAreaElement>(null);
   const [userInput, setUserInput] = useState("");
@@ -492,7 +494,7 @@ export function Chat() {
     // clear search results
     if (n === 0) {
       setPromptHints([]);
-    } else if (!chatStore.config.disablePromptHint && n < SEARCH_TEXT_LIMIT) {
+    } else if (!config.disablePromptHint && n < SEARCH_TEXT_LIMIT) {
       // check if need to trigger auto completion
       if (text.startsWith("/")) {
         let searchText = text.slice(1);
@@ -583,8 +585,6 @@ export function Chat() {
     inputRef.current?.focus();
   };
 
-  const config = useChatStore((state) => state.config);
-
   const context: RenderMessage[] = session.context.slice();
 
   const accessStore = useAccessStore();
@@ -692,10 +692,10 @@ export function Chat() {
           {!isMobileScreen && (
             <div className={styles["window-action-button"]}>
               <IconButton
-                icon={chatStore.config.tightBorder ? <MinIcon /> : <MaxIcon />}
+                icon={config.tightBorder ? <MinIcon /> : <MaxIcon />}
                 bordered
                 onClick={() => {
-                  chatStore.updateConfig(
+                  config.update(
                     (config) => (config.tightBorder = !config.tightBorder),
                   );
                 }}

+ 3 - 3
app/components/home.tsx

@@ -9,7 +9,6 @@ import styles from "./home.module.scss";
 import BotIcon from "../icons/bot.svg";
 import LoadingIcon from "../icons/three-dots.svg";
 
-import { useChatStore } from "../store";
 import { getCSSVar, useMobileScreen } from "../utils";
 import { Chat } from "./chat";
 
@@ -24,6 +23,7 @@ import {
   useLocation,
 } from "react-router-dom";
 import { SideBar } from "./sidebar";
+import { useAppConfig } from "../store/config";
 
 export function Loading(props: { noLogo?: boolean }) {
   return (
@@ -39,7 +39,7 @@ const Settings = dynamic(async () => (await import("./settings")).Settings, {
 });
 
 export function useSwitchTheme() {
-  const config = useChatStore((state) => state.config);
+  const config = useAppConfig();
 
   useEffect(() => {
     document.body.classList.remove("light");
@@ -80,7 +80,7 @@ const useHasHydrated = () => {
 };
 
 function WideScreen() {
-  const config = useChatStore((state) => state.config);
+  const config = useAppConfig();
 
   return (
     <div

+ 8 - 8
app/components/settings.tsx

@@ -23,6 +23,7 @@ import {
   useUpdateStore,
   useAccessStore,
   ModalConfigValidator,
+  useAppConfig,
 } from "../store";
 import { Avatar } from "./chat";
 
@@ -180,14 +181,13 @@ function PasswordInput(props: HTMLProps<HTMLInputElement>) {
 export function Settings() {
   const navigate = useNavigate();
   const [showEmojiPicker, setShowEmojiPicker] = useState(false);
-  const [config, updateConfig, resetConfig, clearAllData, clearSessions] =
-    useChatStore((state) => [
-      state.config,
-      state.updateConfig,
-      state.resetConfig,
-      state.clearAllData,
-      state.clearSessions,
-    ]);
+  const config = useAppConfig();
+  const updateConfig = config.update;
+  const resetConfig = config.reset;
+  const [clearAllData, clearSessions] = useChatStore((state) => [
+    state.clearAllData,
+    state.clearSessions,
+  ]);
 
   const updateStore = useUpdateStore();
   const [checkingUpdate, setCheckingUpdate] = useState(false);

+ 15 - 11
app/components/sidebar.tsx

@@ -1,4 +1,4 @@
-import { useState, useEffect, useRef } from "react";
+import { useEffect, useRef } from "react";
 
 import styles from "./home.module.scss";
 
@@ -10,7 +10,7 @@ import AddIcon from "../icons/add.svg";
 import CloseIcon from "../icons/close.svg";
 import Locale from "../locales";
 
-import { useChatStore } from "../store";
+import { useAppConfig, useChatStore } from "../store";
 
 import {
   MAX_SIDEBAR_WIDTH,
@@ -20,16 +20,20 @@ import {
   REPO_URL,
 } from "../constant";
 
-import { HashRouter as Router, Link, useNavigate } from "react-router-dom";
+import { Link, useNavigate } from "react-router-dom";
 import { useMobileScreen } from "../utils";
-import { ChatList } from "./chat-list";
+import dynamic from "next/dynamic";
+
+const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, {
+  loading: () => null,
+});
 
 function useDragSideBar() {
   const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x);
 
-  const chatStore = useChatStore();
+  const config = useAppConfig();
   const startX = useRef(0);
-  const startDragWidth = useRef(chatStore.config.sidebarWidth ?? 300);
+  const startDragWidth = useRef(config.sidebarWidth ?? 300);
   const lastUpdateTime = useRef(Date.now());
 
   const handleMouseMove = useRef((e: MouseEvent) => {
@@ -39,11 +43,11 @@ function useDragSideBar() {
     lastUpdateTime.current = Date.now();
     const d = e.clientX - startX.current;
     const nextWidth = limit(startDragWidth.current + d);
-    chatStore.updateConfig((config) => (config.sidebarWidth = nextWidth));
+    config.update((config) => (config.sidebarWidth = nextWidth));
   });
 
   const handleMouseUp = useRef(() => {
-    startDragWidth.current = chatStore.config.sidebarWidth ?? 300;
+    startDragWidth.current = config.sidebarWidth ?? 300;
     window.removeEventListener("mousemove", handleMouseMove.current);
     window.removeEventListener("mouseup", handleMouseUp.current);
   });
@@ -56,15 +60,15 @@ function useDragSideBar() {
   };
   const isMobileScreen = useMobileScreen();
   const shouldNarrow =
-    !isMobileScreen && chatStore.config.sidebarWidth < MIN_SIDEBAR_WIDTH;
+    !isMobileScreen && config.sidebarWidth < MIN_SIDEBAR_WIDTH;
 
   useEffect(() => {
     const barWidth = shouldNarrow
       ? NARROW_SIDEBAR_WIDTH
-      : limit(chatStore.config.sidebarWidth ?? 300);
+      : limit(config.sidebarWidth ?? 300);
     const sideBarWidth = isMobileScreen ? "100vw" : `${barWidth}px`;
     document.documentElement.style.setProperty("--sidebar-width", sideBarWidth);
-  }, [chatStore.config.sidebarWidth, isMobileScreen, shouldNarrow]);
+  }, [config.sidebarWidth, isMobileScreen, shouldNarrow]);
 
   return {
     onDragMouseDown,

+ 1 - 1
app/locales/cn.ts

@@ -1,4 +1,4 @@
-import { SubmitKey } from "../store/app";
+import { SubmitKey } from "../store/config";
 
 const cn = {
   WIP: "该功能仍在开发中……",

+ 1 - 1
app/locales/de.ts

@@ -1,4 +1,4 @@
-import { SubmitKey } from "../store/app";
+import { SubmitKey } from "../store/config";
 import type { LocaleType } from "./index";
 
 const de: LocaleType = {

+ 1 - 1
app/locales/en.ts

@@ -1,4 +1,4 @@
-import { SubmitKey } from "../store/app";
+import { SubmitKey } from "../store/config";
 import type { LocaleType } from "./index";
 
 const en: LocaleType = {

+ 1 - 1
app/locales/es.ts

@@ -1,4 +1,4 @@
-import { SubmitKey } from "../store/app";
+import { SubmitKey } from "../store/config";
 import type { LocaleType } from "./index";
 
 const es: LocaleType = {

+ 1 - 1
app/locales/it.ts

@@ -1,4 +1,4 @@
-import { SubmitKey } from "../store/app";
+import { SubmitKey } from "../store/config";
 import type { LocaleType } from "./index";
 
 const it: LocaleType = {

+ 1 - 1
app/locales/jp.ts

@@ -1,4 +1,4 @@
-import { SubmitKey } from "../store/app";
+import { SubmitKey } from "../store/config";
 
 const jp = {
   WIP: "この機能は開発中です……",

+ 1 - 1
app/locales/tr.ts

@@ -1,4 +1,4 @@
-import { SubmitKey } from "../store/app";
+import { SubmitKey } from "../store/config";
 import type { LocaleType } from "./index";
 
 const tr: LocaleType = {

+ 1 - 1
app/locales/tw.ts

@@ -1,4 +1,4 @@
-import { SubmitKey } from "../store/app";
+import { SubmitKey } from "../store/config";
 import type { LocaleType } from "./index";
 
 const tw: LocaleType = {

+ 2 - 1
app/requests.ts

@@ -4,6 +4,7 @@ import {
   ModelConfig,
   ModelType,
   useAccessStore,
+  useAppConfig,
   useChatStore,
 } from "./store";
 import { showToast } from "./components/ui-lib";
@@ -27,7 +28,7 @@ const makeRequestParam = (
     sendMessages = sendMessages.filter((m) => m.role !== "assistant");
   }
 
-  const modelConfig = { ...useChatStore.getState().config.modelConfig };
+  const modelConfig = { ...useAppConfig.getState().modelConfig };
 
   // @yidadaa: wont send max_tokens, because it is nonsense for Muggles
   // @ts-expect-error

+ 7 - 152
app/store/app.ts

@@ -11,6 +11,7 @@ import { isMobileScreen, trimTopic } from "../utils";
 
 import Locale from "../locales";
 import { showToast } from "../components/ui-lib";
+import { ModelType, useAppConfig } from "./config";
 
 export type Message = ChatCompletionResponseMessage & {
   date: string;
@@ -30,133 +31,8 @@ export function createMessage(override: Partial<Message>): Message {
   };
 }
 
-export enum SubmitKey {
-  Enter = "Enter",
-  CtrlEnter = "Ctrl + Enter",
-  ShiftEnter = "Shift + Enter",
-  AltEnter = "Alt + Enter",
-  MetaEnter = "Meta + Enter",
-}
-
-export enum Theme {
-  Auto = "auto",
-  Dark = "dark",
-  Light = "light",
-}
-
-export interface ChatConfig {
-  historyMessageCount: number; // -1 means all
-  compressMessageLengthThreshold: number;
-  sendBotMessages: boolean; // send bot's message or not
-  submitKey: SubmitKey;
-  avatar: string;
-  fontSize: number;
-  theme: Theme;
-  tightBorder: boolean;
-  sendPreviewBubble: boolean;
-  sidebarWidth: number;
-
-  disablePromptHint: boolean;
-
-  modelConfig: {
-    model: ModelType;
-    temperature: number;
-    max_tokens: number;
-    presence_penalty: number;
-  };
-}
-
-export type ModelConfig = ChatConfig["modelConfig"];
-
 export const ROLES: Message["role"][] = ["system", "user", "assistant"];
 
-const ENABLE_GPT4 = true;
-
-export const ALL_MODELS = [
-  {
-    name: "gpt-4",
-    available: ENABLE_GPT4,
-  },
-  {
-    name: "gpt-4-0314",
-    available: ENABLE_GPT4,
-  },
-  {
-    name: "gpt-4-32k",
-    available: ENABLE_GPT4,
-  },
-  {
-    name: "gpt-4-32k-0314",
-    available: ENABLE_GPT4,
-  },
-  {
-    name: "gpt-3.5-turbo",
-    available: true,
-  },
-  {
-    name: "gpt-3.5-turbo-0301",
-    available: true,
-  },
-] as const;
-
-export type ModelType = (typeof ALL_MODELS)[number]["name"];
-
-export function limitNumber(
-  x: number,
-  min: number,
-  max: number,
-  defaultValue: number,
-) {
-  if (typeof x !== "number" || isNaN(x)) {
-    return defaultValue;
-  }
-
-  return Math.min(max, Math.max(min, x));
-}
-
-export function limitModel(name: string) {
-  return ALL_MODELS.some((m) => m.name === name && m.available)
-    ? name
-    : ALL_MODELS[4].name;
-}
-
-export const ModalConfigValidator = {
-  model(x: string) {
-    return limitModel(x) as ModelType;
-  },
-  max_tokens(x: number) {
-    return limitNumber(x, 0, 32000, 2000);
-  },
-  presence_penalty(x: number) {
-    return limitNumber(x, -2, 2, 0);
-  },
-  temperature(x: number) {
-    return limitNumber(x, 0, 2, 1);
-  },
-};
-
-const DEFAULT_CONFIG: ChatConfig = {
-  historyMessageCount: 4,
-  compressMessageLengthThreshold: 1000,
-  sendBotMessages: true as boolean,
-  submitKey: SubmitKey.CtrlEnter as SubmitKey,
-  avatar: "1f603",
-  fontSize: 14,
-  theme: Theme.Auto as Theme,
-  tightBorder: false,
-  sendPreviewBubble: true,
-  sidebarWidth: 300,
-
-  disablePromptHint: false,
-
-  modelConfig: {
-    model: "gpt-3.5-turbo",
-    temperature: 1,
-    max_tokens: 2000,
-    presence_penalty: 0,
-  },
-};
-
 export interface ChatStat {
   tokenCount: number;
   wordCount: number;
@@ -202,7 +78,6 @@ function createEmptySession(): ChatSession {
 }
 
 interface ChatStore {
-  config: ChatConfig;
   sessions: ChatSession[];
   currentSessionIndex: number;
   clearSessions: () => void;
@@ -226,9 +101,6 @@ interface ChatStore {
   getMessagesWithMemory: () => Message[];
   getMemoryPrompt: () => Message;
 
-  getConfig: () => ChatConfig;
-  resetConfig: () => void;
-  updateConfig: (updater: (config: ChatConfig) => void) => void;
   clearAllData: () => void;
 }
 
@@ -243,9 +115,6 @@ export const useChatStore = create<ChatStore>()(
     (set, get) => ({
       sessions: [createEmptySession()],
       currentSessionIndex: 0,
-      config: {
-        ...DEFAULT_CONFIG,
-      },
 
       clearSessions() {
         set(() => ({
@@ -254,20 +123,6 @@ export const useChatStore = create<ChatStore>()(
         }));
       },
 
-      resetConfig() {
-        set(() => ({ config: { ...DEFAULT_CONFIG } }));
-      },
-
-      getConfig() {
-        return get().config;
-      },
-
-      updateConfig(updater) {
-        const config = get().config;
-        updater(config);
-        set(() => ({ config }));
-      },
-
       selectSession(index: number) {
         set({
           currentSessionIndex: index,
@@ -390,7 +245,7 @@ export const useChatStore = create<ChatStore>()(
           role: "assistant",
           streaming: true,
           id: userMessage.id! + 1,
-          model: get().config.modelConfig.model,
+          model: useAppConfig.getState().modelConfig.model,
         });
 
         // get recent messages
@@ -443,8 +298,8 @@ export const useChatStore = create<ChatStore>()(
               controller,
             );
           },
-          filterBot: !get().config.sendBotMessages,
-          modelConfig: get().config.modelConfig,
+          filterBot: !useAppConfig.getState().sendBotMessages,
+          modelConfig: useAppConfig.getState().modelConfig,
         });
       },
 
@@ -460,7 +315,7 @@ export const useChatStore = create<ChatStore>()(
 
       getMessagesWithMemory() {
         const session = get().currentSession();
-        const config = get().config;
+        const config = useAppConfig.getState();
         const messages = session.messages.filter((msg) => !msg.isError);
         const n = messages.length;
 
@@ -545,14 +400,14 @@ export const useChatStore = create<ChatStore>()(
           });
         }
 
-        const config = get().config;
+        const config = useAppConfig.getState();
         let toBeSummarizedMsgs = session.messages.slice(
           session.lastSummarizeIndex,
         );
 
         const historyMsgLength = countMessages(toBeSummarizedMsgs);
 
-        if (historyMsgLength > get().config?.modelConfig?.max_tokens ?? 4000) {
+        if (historyMsgLength > config?.modelConfig?.max_tokens ?? 4000) {
           const n = toBeSummarizedMsgs.length;
           toBeSummarizedMsgs = toBeSummarizedMsgs.slice(
             Math.max(0, n - config.historyMessageCount),

+ 135 - 0
app/store/config.ts

@@ -0,0 +1,135 @@
+import { create } from "zustand";
+import { persist } from "zustand/middleware";
+
+export enum SubmitKey {
+  Enter = "Enter",
+  CtrlEnter = "Ctrl + Enter",
+  ShiftEnter = "Shift + Enter",
+  AltEnter = "Alt + Enter",
+  MetaEnter = "Meta + Enter",
+}
+
+export enum Theme {
+  Auto = "auto",
+  Dark = "dark",
+  Light = "light",
+}
+
+const DEFAULT_CONFIG = {
+  historyMessageCount: 4,
+  compressMessageLengthThreshold: 1000,
+  sendBotMessages: true as boolean,
+  submitKey: SubmitKey.CtrlEnter as SubmitKey,
+  avatar: "1f603",
+  fontSize: 14,
+  theme: Theme.Auto as Theme,
+  tightBorder: false,
+  sendPreviewBubble: true,
+  sidebarWidth: 300,
+
+  disablePromptHint: false,
+
+  modelConfig: {
+    model: "gpt-3.5-turbo" as ModelType,
+    temperature: 1,
+    max_tokens: 2000,
+    presence_penalty: 0,
+  },
+};
+
+export type ChatConfig = typeof DEFAULT_CONFIG;
+
+export type ChatConfigStore = ChatConfig & {
+  reset: () => void;
+  update: (updater: (config: ChatConfig) => void) => void;
+};
+
+export type ModelConfig = ChatConfig["modelConfig"];
+
+const ENABLE_GPT4 = true;
+
+export const ALL_MODELS = [
+  {
+    name: "gpt-4",
+    available: ENABLE_GPT4,
+  },
+  {
+    name: "gpt-4-0314",
+    available: ENABLE_GPT4,
+  },
+  {
+    name: "gpt-4-32k",
+    available: ENABLE_GPT4,
+  },
+  {
+    name: "gpt-4-32k-0314",
+    available: ENABLE_GPT4,
+  },
+  {
+    name: "gpt-3.5-turbo",
+    available: true,
+  },
+  {
+    name: "gpt-3.5-turbo-0301",
+    available: true,
+  },
+] as const;
+
+export type ModelType = (typeof ALL_MODELS)[number]["name"];
+
+export function limitNumber(
+  x: number,
+  min: number,
+  max: number,
+  defaultValue: number,
+) {
+  if (typeof x !== "number" || isNaN(x)) {
+    return defaultValue;
+  }
+
+  return Math.min(max, Math.max(min, x));
+}
+
+export function limitModel(name: string) {
+  return ALL_MODELS.some((m) => m.name === name && m.available)
+    ? name
+    : ALL_MODELS[4].name;
+}
+
+export const ModalConfigValidator = {
+  model(x: string) {
+    return limitModel(x) as ModelType;
+  },
+  max_tokens(x: number) {
+    return limitNumber(x, 0, 32000, 2000);
+  },
+  presence_penalty(x: number) {
+    return limitNumber(x, -2, 2, 0);
+  },
+  temperature(x: number) {
+    return limitNumber(x, 0, 2, 1);
+  },
+};
+
+const CONFIG_KEY = "app-config";
+
+export const useAppConfig = create<ChatConfigStore>()(
+  persist(
+    (set, get) => ({
+      ...DEFAULT_CONFIG,
+
+      reset() {
+        set(() => ({ ...DEFAULT_CONFIG }));
+      },
+
+      update(updater) {
+        const config = { ...get() };
+        updater(config);
+        set(() => config);
+      },
+    }),
+    {
+      name: CONFIG_KEY,
+    },
+  ),
+);

+ 1 - 0
app/store/index.ts

@@ -1,3 +1,4 @@
 export * from "./app";
 export * from "./update";
 export * from "./access";
+export * from "./config";