diff --git a/app/components/settings.module.scss b/app/components/settings.module.scss index ad994f68..7d40d83b 100644 --- a/app/components/settings.module.scss +++ b/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; + } +} diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 936588b7..4645f319 100644 --- a/app/components/settings.tsx +++ b/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) { } return ( - - +
: } onClick={changeVisibility} + className={styles["password-eye"]} /> - + +
); } @@ -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 ( - <> +
@@ -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 }) { > void }) { updateConfig( (config) => (config.modelConfig.temperature = - e.currentTarget.valueAsNumber), + ModalConfigValidator.temperature( + e.currentTarget.valueAsNumber, + )), ); }} > @@ -490,13 +495,15 @@ export function Settings(props: { closeSettings: () => void }) { updateConfig( (config) => (config.modelConfig.max_tokens = - e.currentTarget.valueAsNumber), + ModalConfigValidator.max_tokens( + e.currentTarget.valueAsNumber, + )), ) } > @@ -507,7 +514,7 @@ export function Settings(props: { closeSettings: () => void }) { > void }) { updateConfig( (config) => (config.modelConfig.presence_penalty = - e.currentTarget.valueAsNumber), + ModalConfigValidator.presence_penalty( + e.currentTarget.valueAsNumber, + )), ); }} >
- + ); } diff --git a/app/requests.ts b/app/requests.ts index ee3103b7..da9b5c97 100644 --- a/app/requests.ts +++ b/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(); diff --git a/app/store/app.ts b/app/store/app.ts index b943c0d9..d01e3cdd 100644 --- a/app/store/app.ts +++ b/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 { - 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,