Merge pull request #1361 from Yidadaa/bugfix-0509

feat: #1055, #444 and fix #1359 hot key to switch chat and allow to disable gpt-4
This commit is contained in:
Yifei Zhang 2023-05-09 23:39:53 +08:00 committed by GitHub
commit dae7da0e4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 81 additions and 19 deletions

View File

@ -168,7 +168,13 @@ Specify OpenAI organization ID.
> Default: Empty
If you do not want users to input their own API key, set this environment variable to 1.
If you do not want users to input their own API key, set this value to 1.
### `DISABLE_GPT4` (optional)
> Default: Empty
If you do not want users to use GPT-4, set this value to 1.
## Development

View File

@ -64,7 +64,7 @@ code1,code2,code3
## 环境变量
> 本项目大多数配置项都通过环境变量来设置。
> 本项目大多数配置项都通过环境变量来设置,教程:[如何修改 Vercel 环境变量](./docs/vercel-cn.md)
### `OPENAI_API_KEY` (必填项)
@ -94,6 +94,10 @@ OpenAI 接口代理 URL如果你手动配置了 openai 接口代理,请填
如果你不想让用户自行填入 API Key将此环境变量设置为 1 即可。
### `DISABLE_GPT4` (可选)
如果你不想让用户使用 GPT-4将此环境变量设置为 1 即可。
## 开发
> 强烈不建议在本地进行开发或者部署,由于一些技术原因,很难在本地配置好 OpenAI API 代理,除非你能保证可以直连 OpenAI 服务器。

View File

@ -1,4 +1,4 @@
import { NextRequest, NextResponse } from "next/server";
import { NextResponse } from "next/server";
import { getServerSideConfig } from "../../config/server";
@ -9,6 +9,7 @@ const serverConfig = getServerSideConfig();
const DANGER_CONFIG = {
needCode: serverConfig.needCode,
hideUserApiKey: serverConfig.hideUserApiKey,
enableGPT4: serverConfig.enableGPT4,
};
declare global {

View File

@ -53,7 +53,7 @@ import chatStyle from "./chat.module.scss";
import { ListItem, Modal, showModal } from "./ui-lib";
import { useLocation, useNavigate } from "react-router-dom";
import { Path } from "../constant";
import { LAST_INPUT_KEY, Path } from "../constant";
import { Avatar } from "./emoji";
import { MaskAvatar, MaskConfig } from "./mask";
import { useMaskStore } from "../store/mask";
@ -404,7 +404,6 @@ export function Chat() {
const inputRef = useRef<HTMLTextAreaElement>(null);
const [userInput, setUserInput] = useState("");
const [beforeInput, setBeforeInput] = useState("");
const [isLoading, setIsLoading] = useState(false);
const { submitKey, shouldSubmit } = useSubmitHandler();
const { scrollRef, setAutoScroll, scrollToBottom } = useScrollToBottom();
@ -477,7 +476,7 @@ export function Chat() {
if (userInput.trim() === "") return;
setIsLoading(true);
chatStore.onUserInput(userInput).then(() => setIsLoading(false));
setBeforeInput(userInput);
localStorage.setItem(LAST_INPUT_KEY, userInput);
setUserInput("");
setPromptHints([]);
if (!isMobileScreen) inputRef.current?.focus();
@ -491,9 +490,9 @@ export function Chat() {
// check if should send message
const onInputKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
// if ArrowUp and no userInput
// if ArrowUp and no userInput, fill with last input
if (e.key === "ArrowUp" && userInput.length <= 0) {
setUserInput(beforeInput);
setUserInput(localStorage.getItem(LAST_INPUT_KEY) ?? "");
e.preventDefault();
return;
}
@ -503,11 +502,6 @@ export function Chat() {
}
};
const onRightClick = (e: any, message: Message) => {
// auto fill user input
if (message.role === "user") {
setUserInput(message.content);
}
// copy to clipboard
if (selectOrCopy(e.currentTarget, message.content)) {
e.preventDefault();

View File

@ -14,7 +14,7 @@ import CopyIcon from "../icons/copy.svg";
import { DEFAULT_MASK_AVATAR, Mask, useMaskStore } from "../store/mask";
import { Message, ModelConfig, ROLES, useChatStore } from "../store";
import { Input, List, ListItem, Modal, Popover, showToast } from "./ui-lib";
import { Input, List, ListItem, Modal, Popover } from "./ui-lib";
import { Avatar, AvatarPicker } from "./emoji";
import Locale, { AllLangs, Lang } from "../locales";
import { useNavigate } from "react-router-dom";

View File

@ -32,6 +32,28 @@ const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, {
loading: () => null,
});
function useHotKey() {
const chatStore = useChatStore();
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
if (e.metaKey || e.altKey || e.ctrlKey) {
const n = chatStore.sessions.length;
const limit = (x: number) => (x + n) % n;
const i = chatStore.currentSessionIndex;
if (e.key === "ArrowUp") {
chatStore.selectSession(limit(i - 1));
} else if (e.key === "ArrowDown") {
chatStore.selectSession(limit(i + 1));
}
}
};
window.addEventListener("keydown", onKeyDown);
return () => window.removeEventListener("keydown", onKeyDown);
});
}
function useDragSideBar() {
const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x);
@ -86,9 +108,10 @@ export function SideBar(props: { className?: string }) {
// drag side bar
const { onDragMouseDown, shouldNarrow } = useDragSideBar();
const navigate = useNavigate();
const config = useAppConfig();
useHotKey();
return (
<div
className={`${styles.sidebar} ${props.className} ${

View File

@ -8,6 +8,7 @@ declare global {
PROXY_URL?: string;
VERCEL?: string;
HIDE_USER_API_KEY?: string; // disable user's api key input
DISABLE_GPT4?: string; // allow user to use gpt-4 or not
}
}
}
@ -40,5 +41,6 @@ export const getServerSideConfig = () => {
proxyUrl: process.env.PROXY_URL,
isVercel: !!process.env.VERCEL,
hideUserApiKey: !!process.env.HIDE_USER_API_KEY,
enableGPT4: !process.env.DISABLE_GPT4,
};
};

View File

@ -38,3 +38,5 @@ export const MIN_SIDEBAR_WIDTH = 230;
export const NARROW_SIDEBAR_WIDTH = 100;
export const ACCESS_CODE_PREFIX = "ak-";
export const LAST_INPUT_KEY = "last-input";

View File

@ -31,7 +31,7 @@ export const EN_MASKS: BuiltinMask[] = [
],
modelConfig: {
model: "gpt-4",
temperature: 1,
temperature: 0.5,
max_tokens: 2000,
presence_penalty: 0,
sendMemory: true,

View File

@ -2,6 +2,7 @@ import { create } from "zustand";
import { persist } from "zustand/middleware";
import { StoreKey } from "../constant";
import { BOT_HELLO } from "./chat";
import { ALL_MODELS } from "./config";
export interface AccessControlStore {
accessCode: string;
@ -60,6 +61,14 @@ export const useAccessStore = create<AccessControlStore>()(
console.log("[Config] got config from server", res);
set(() => ({ ...res }));
if (!res.enableGPT4) {
ALL_MODELS.forEach((model) => {
if (model.name.startsWith("gpt-4")) {
(model as any).available = false;
}
});
}
if ((res as any).botHello) {
BOT_HELLO.content = (res as any).botHello;
}

View File

@ -180,8 +180,9 @@ export const useChatStore = create<ChatStore>()(
const sessions = get().sessions.slice();
sessions.splice(index, 1);
const currentIndex = get().currentSessionIndex;
let nextIndex = Math.min(
get().currentSessionIndex,
currentIndex - Number(index < currentIndex),
sessions.length - 1,
);

View File

@ -76,6 +76,26 @@ export const ALL_MODELS = [
name: "gpt-3.5-turbo-0301",
available: true,
},
{
name: "qwen-v1", // 通义千问
available: false,
},
{
name: "ernie", // 文心一言
available: false,
},
{
name: "spark", // 讯飞星火
available: false,
},
{
name: "llama", // llama
available: false,
},
{
name: "chatglm", // chatglm-6b
available: false,
},
] as const;
export type ModelType = (typeof ALL_MODELS)[number]["name"];

View File

@ -160,13 +160,13 @@ export function autoGrowTextArea(dom: HTMLTextAreaElement) {
measureDom.style.width = width + "px";
measureDom.innerText = dom.value.trim().length > 0 ? dom.value : "1";
const lineWrapCount = Math.max(0, dom.value.split("\n").length - 1);
const emptyLineWrap = Math.max(0, dom.value.split("\n\n").length - 1);
const height = parseFloat(window.getComputedStyle(measureDom).height);
const singleLineHeight = parseFloat(
window.getComputedStyle(singleLineDom).height,
);
const rows = Math.round(height / singleLineHeight) + lineWrapCount;
const rows = Math.round(height / singleLineHeight) + emptyLineWrap;
return rows;
}