forked from XiaoMo/ChatGPT-Next-Web
fix: #418 valid model config
This commit is contained in:
parent
7572c99f4d
commit
4e644cfca7
@ -18,3 +18,12 @@
|
|||||||
.avatar {
|
.avatar {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.password-input {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.password-eye {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,6 +21,7 @@ import {
|
|||||||
ALL_MODELS,
|
ALL_MODELS,
|
||||||
useUpdateStore,
|
useUpdateStore,
|
||||||
useAccessStore,
|
useAccessStore,
|
||||||
|
ModalConfigValidator,
|
||||||
} from "../store";
|
} from "../store";
|
||||||
import { Avatar } from "./chat";
|
import { Avatar } from "./chat";
|
||||||
|
|
||||||
@ -30,6 +31,7 @@ import Link from "next/link";
|
|||||||
import { UPDATE_URL } from "../constant";
|
import { UPDATE_URL } from "../constant";
|
||||||
import { SearchService, usePromptStore } from "../store/prompt";
|
import { SearchService, usePromptStore } from "../store/prompt";
|
||||||
import { requestUsage } from "../requests";
|
import { requestUsage } from "../requests";
|
||||||
|
import { ErrorBoundary } from "./error";
|
||||||
|
|
||||||
function SettingItem(props: {
|
function SettingItem(props: {
|
||||||
title: string;
|
title: string;
|
||||||
@ -57,17 +59,14 @@ function PasswordInput(props: HTMLProps<HTMLInputElement>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span style={{ display: "flex", justifyContent: "end" }}>
|
<div className={styles["password-input"]}>
|
||||||
<input
|
|
||||||
{...props}
|
|
||||||
style={{ minWidth: "150px" }}
|
|
||||||
type={visible ? "text" : "password"}
|
|
||||||
/>
|
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={visible ? <EyeIcon /> : <EyeOffIcon />}
|
icon={visible ? <EyeIcon /> : <EyeOffIcon />}
|
||||||
onClick={changeVisibility}
|
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(() => {
|
useEffect(() => {
|
||||||
checkUpdate();
|
checkUpdate();
|
||||||
checkUsage();
|
checkUsage();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const accessStore = useAccessStore();
|
const accessStore = useAccessStore();
|
||||||
const enabledAccessControl = useMemo(
|
const enabledAccessControl = useMemo(
|
||||||
() => accessStore.enabledAccessControl(),
|
() => accessStore.enabledAccessControl(),
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -135,7 +136,7 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
}, [showUsage]);
|
}, [showUsage]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<ErrorBoundary>
|
||||||
<div className={styles["window-header"]}>
|
<div className={styles["window-header"]}>
|
||||||
<div className={styles["window-header-title"]}>
|
<div className={styles["window-header-title"]}>
|
||||||
<div className={styles["window-header-main-title"]}>
|
<div className={styles["window-header-main-title"]}>
|
||||||
@ -453,7 +454,9 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
updateConfig(
|
updateConfig(
|
||||||
(config) =>
|
(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
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
value={config.modelConfig.temperature.toFixed(1)}
|
value={config.modelConfig.temperature?.toFixed(1)}
|
||||||
min="0"
|
min="0"
|
||||||
max="2"
|
max="2"
|
||||||
step="0.1"
|
step="0.1"
|
||||||
@ -478,7 +481,9 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
updateConfig(
|
updateConfig(
|
||||||
(config) =>
|
(config) =>
|
||||||
(config.modelConfig.temperature =
|
(config.modelConfig.temperature =
|
||||||
e.currentTarget.valueAsNumber),
|
ModalConfigValidator.temperature(
|
||||||
|
e.currentTarget.valueAsNumber,
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
></input>
|
></input>
|
||||||
@ -490,13 +495,15 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min={100}
|
min={100}
|
||||||
max={4096}
|
max={32000}
|
||||||
value={config.modelConfig.max_tokens}
|
value={config.modelConfig.max_tokens}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateConfig(
|
updateConfig(
|
||||||
(config) =>
|
(config) =>
|
||||||
(config.modelConfig.max_tokens =
|
(config.modelConfig.max_tokens =
|
||||||
e.currentTarget.valueAsNumber),
|
ModalConfigValidator.max_tokens(
|
||||||
|
e.currentTarget.valueAsNumber,
|
||||||
|
)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
></input>
|
></input>
|
||||||
@ -507,7 +514,7 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
value={config.modelConfig.presence_penalty.toFixed(1)}
|
value={config.modelConfig.presence_penalty?.toFixed(1)}
|
||||||
min="-2"
|
min="-2"
|
||||||
max="2"
|
max="2"
|
||||||
step="0.5"
|
step="0.5"
|
||||||
@ -515,13 +522,15 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
updateConfig(
|
updateConfig(
|
||||||
(config) =>
|
(config) =>
|
||||||
(config.modelConfig.presence_penalty =
|
(config.modelConfig.presence_penalty =
|
||||||
e.currentTarget.valueAsNumber),
|
ModalConfigValidator.presence_penalty(
|
||||||
|
e.currentTarget.valueAsNumber,
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
></input>
|
></input>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { ChatRequest, ChatReponse } from "./api/openai/typing";
|
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 Locale from "./locales";
|
||||||
import { showToast } from "./components/ui-lib";
|
import { showToast } from "./components/ui-lib";
|
||||||
|
|
||||||
@ -123,11 +123,6 @@ export async function requestChatStream(
|
|||||||
filterBot: options?.filterBot,
|
filterBot: options?.filterBot,
|
||||||
});
|
});
|
||||||
|
|
||||||
// valid and assign model config
|
|
||||||
if (options?.modelConfig) {
|
|
||||||
Object.assign(req, filterConfig(options.modelConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("[Request] ", req);
|
console.log("[Request] ", req);
|
||||||
|
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
|
@ -85,44 +85,40 @@ export const ALL_MODELS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function isValidModel(name: string) {
|
export function limitNumber(
|
||||||
return ALL_MODELS.some((m) => m.name === name && m.available);
|
x: number,
|
||||||
}
|
min: number,
|
||||||
|
max: number,
|
||||||
export function isValidNumber(x: number, min: number, max: number) {
|
defaultValue: number,
|
||||||
return typeof x === "number" && x <= max && x >= min;
|
) {
|
||||||
}
|
if (typeof x !== "number" || isNaN(x)) {
|
||||||
|
return defaultValue;
|
||||||
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;
|
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);
|
||||||
|
},
|
||||||
|
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 = {
|
const DEFAULT_CONFIG: ChatConfig = {
|
||||||
historyMessageCount: 4,
|
historyMessageCount: 4,
|
||||||
compressMessageLengthThreshold: 1000,
|
compressMessageLengthThreshold: 1000,
|
||||||
|
Loading…
Reference in New Issue
Block a user