Browse Source

fix: #418 valid model config

yidadaa 1 year ago
parent
commit
4e644cfca7
4 changed files with 64 additions and 55 deletions
  1. 9 0
      app/components/settings.module.scss
  2. 25 16
      app/components/settings.tsx
  3. 1 6
      app/requests.ts
  4. 29 33
      app/store/app.ts

+ 9 - 0
app/components/settings.module.scss

@@ -18,3 +18,12 @@
 .avatar {
   cursor: pointer;
 }
+
+.password-input {
+  display: flex;
+  justify-content: flex-end;
+
+  .password-eye {
+    margin-right: 4px;
+  }
+}

+ 25 - 16
app/components/settings.tsx

@@ -21,6 +21,7 @@ import {
   ALL_MODELS,
   useUpdateStore,
   useAccessStore,
+  ModalConfigValidator,
 } from "../store";
 import { Avatar } from "./chat";
 
@@ -30,6 +31,7 @@ import Link from "next/link";
 import { UPDATE_URL } from "../constant";
 import { SearchService, usePromptStore } from "../store/prompt";
 import { requestUsage } from "../requests";
+import { ErrorBoundary } from "./error";
 
 function SettingItem(props: {
   title: string;
@@ -57,17 +59,14 @@ function PasswordInput(props: HTMLProps<HTMLInputElement>) {
   }
 
   return (
-    <span style={{ display: "flex", justifyContent: "end" }}>
-      <input
-        {...props}
-        style={{ minWidth: "150px" }}
-        type={visible ? "text" : "password"}
-      />
+    <div className={styles["password-input"]}>
       <IconButton
         icon={visible ? <EyeIcon /> : <EyeOffIcon />}
         onClick={changeVisibility}
+        className={styles["password-eye"]}
       />
-    </span>
+      <input {...props} type={visible ? "text" : "password"} />
+    </div>
   );
 }
 
@@ -115,11 +114,13 @@ export function Settings(props: { closeSettings: () => void }) {
   useEffect(() => {
     checkUpdate();
     checkUsage();
+    // eslint-disable-next-line react-hooks/exhaustive-deps
   }, []);
 
   const accessStore = useAccessStore();
   const enabledAccessControl = useMemo(
     () => accessStore.enabledAccessControl(),
+    // eslint-disable-next-line react-hooks/exhaustive-deps
     [],
   );
 
@@ -135,7 +136,7 @@ export function Settings(props: { closeSettings: () => void }) {
   }, [showUsage]);
 
   return (
-    <>
+    <ErrorBoundary>
       <div className={styles["window-header"]}>
         <div className={styles["window-header-title"]}>
           <div className={styles["window-header-main-title"]}>
@@ -453,7 +454,9 @@ export function Settings(props: { closeSettings: () => void }) {
               onChange={(e) => {
                 updateConfig(
                   (config) =>
-                    (config.modelConfig.model = e.currentTarget.value),
+                    (config.modelConfig.model = ModalConfigValidator.model(
+                      e.currentTarget.value,
+                    )),
                 );
               }}
             >
@@ -470,7 +473,7 @@ export function Settings(props: { closeSettings: () => void }) {
           >
             <input
               type="range"
-              value={config.modelConfig.temperature.toFixed(1)}
+              value={config.modelConfig.temperature?.toFixed(1)}
               min="0"
               max="2"
               step="0.1"
@@ -478,7 +481,9 @@ export function Settings(props: { closeSettings: () => void }) {
                 updateConfig(
                   (config) =>
                     (config.modelConfig.temperature =
-                      e.currentTarget.valueAsNumber),
+                      ModalConfigValidator.temperature(
+                        e.currentTarget.valueAsNumber,
+                      )),
                 );
               }}
             ></input>
@@ -490,13 +495,15 @@ export function Settings(props: { closeSettings: () => void }) {
             <input
               type="number"
               min={100}
-              max={4096}
+              max={32000}
               value={config.modelConfig.max_tokens}
               onChange={(e) =>
                 updateConfig(
                   (config) =>
                     (config.modelConfig.max_tokens =
-                      e.currentTarget.valueAsNumber),
+                      ModalConfigValidator.max_tokens(
+                        e.currentTarget.valueAsNumber,
+                      )),
                 )
               }
             ></input>
@@ -507,7 +514,7 @@ export function Settings(props: { closeSettings: () => void }) {
           >
             <input
               type="range"
-              value={config.modelConfig.presence_penalty.toFixed(1)}
+              value={config.modelConfig.presence_penalty?.toFixed(1)}
               min="-2"
               max="2"
               step="0.5"
@@ -515,13 +522,15 @@ export function Settings(props: { closeSettings: () => void }) {
                 updateConfig(
                   (config) =>
                     (config.modelConfig.presence_penalty =
-                      e.currentTarget.valueAsNumber),
+                      ModalConfigValidator.presence_penalty(
+                        e.currentTarget.valueAsNumber,
+                      )),
                 );
               }}
             ></input>
           </SettingItem>
         </List>
       </div>
-    </>
+    </ErrorBoundary>
   );
 }

+ 1 - 6
app/requests.ts

@@ -1,5 +1,5 @@
 import type { ChatRequest, ChatReponse } from "./api/openai/typing";
-import { filterConfig, Message, ModelConfig, useAccessStore } from "./store";
+import { Message, ModelConfig, useAccessStore } from "./store";
 import Locale from "./locales";
 import { showToast } from "./components/ui-lib";
 
@@ -123,11 +123,6 @@ export async function requestChatStream(
     filterBot: options?.filterBot,
   });
 
-  // valid and assign model config
-  if (options?.modelConfig) {
-    Object.assign(req, filterConfig(options.modelConfig));
-  }
-
   console.log("[Request] ", req);
 
   const controller = new AbortController();

+ 29 - 33
app/store/app.ts

@@ -85,43 +85,39 @@ export const ALL_MODELS = [
   },
 ];
 
-export function isValidModel(name: string) {
-  return ALL_MODELS.some((m) => m.name === name && m.available);
+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 isValidNumber(x: number, min: number, max: number) {
-  return typeof x === "number" && x <= max && x >= min;
+export function limitModel(name: string) {
+  return ALL_MODELS.some((m) => m.name === name && m.available)
+    ? name
+    : ALL_MODELS[4].name;
 }
 
-export function filterConfig(oldConfig: ModelConfig): Partial<ModelConfig> {
-  const config = Object.assign({}, oldConfig);
-
-  const validator: {
-    [k in keyof ModelConfig]: (x: ModelConfig[keyof ModelConfig]) => boolean;
-  } = {
-    model(x) {
-      return isValidModel(x as string);
-    },
-    max_tokens(x) {
-      return isValidNumber(x as number, 100, 32000);
-    },
-    presence_penalty(x) {
-      return isValidNumber(x as number, -2, 2);
-    },
-    temperature(x) {
-      return isValidNumber(x as number, 0, 2);
-    },
-  };
-
-  Object.keys(validator).forEach((k) => {
-    const key = k as keyof ModelConfig;
-    if (!validator[key](config[key])) {
-      delete config[key];
-    }
-  });
-
-  return config;
-}
+export const ModalConfigValidator = {
+  model(x: string) {
+    return limitModel(x);
+  },
+  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,