This commit is contained in:
GH Action - Upstream Sync 2023-05-10 00:57:53 +00:00
commit d29b7fa1c7
13 changed files with 97 additions and 20 deletions

View File

@ -83,6 +83,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel.
## 最新动态 ## 最新动态
- 🚀 v2.0 已经发布,现在你可以使用面具功能快速创建预制对话了! 了解更多: [ChatGPT 提示词高阶技能:零次、一次和少样本提示](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138)。 - 🚀 v2.0 已经发布,现在你可以使用面具功能快速创建预制对话了! 了解更多: [ChatGPT 提示词高阶技能:零次、一次和少样本提示](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138)。
- 💡 想要更方便地随时随地使用本项目可以试下这款桌面插件https://github.com/mushan0x0/AI0x0.com
## Get Started ## Get Started
@ -167,7 +168,13 @@ Specify OpenAI organization ID.
> Default: Empty > 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 ## Development
@ -255,6 +262,9 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s
[@WingCH](https://github.com/WingCH) [@WingCH](https://github.com/WingCH)
[@jtung4](https://github.com/jtung4) [@jtung4](https://github.com/jtung4)
[@micozhu](https://github.com/micozhu) [@micozhu](https://github.com/micozhu)
[@jhansion](https://github.com/jhansion)
[@Sha1rholder](https://github.com/Sha1rholder)
[@AnsonHyq](https://github.com/AnsonHyq)
### Contributor ### Contributor

View File

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

View File

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

View File

@ -14,7 +14,7 @@ import CopyIcon from "../icons/copy.svg";
import { DEFAULT_MASK_AVATAR, Mask, useMaskStore } from "../store/mask"; import { DEFAULT_MASK_AVATAR, Mask, useMaskStore } from "../store/mask";
import { Message, ModelConfig, ROLES, useChatStore } from "../store"; 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 { Avatar, AvatarPicker } from "./emoji";
import Locale, { AllLangs, Lang } from "../locales"; import Locale, { AllLangs, Lang } from "../locales";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";

View File

@ -32,6 +32,28 @@ const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, {
loading: () => null, 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() { function useDragSideBar() {
const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x); const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x);
@ -86,9 +108,10 @@ export function SideBar(props: { className?: string }) {
// drag side bar // drag side bar
const { onDragMouseDown, shouldNarrow } = useDragSideBar(); const { onDragMouseDown, shouldNarrow } = useDragSideBar();
const navigate = useNavigate(); const navigate = useNavigate();
const config = useAppConfig(); const config = useAppConfig();
useHotKey();
return ( return (
<div <div
className={`${styles.sidebar} ${props.className} ${ className={`${styles.sidebar} ${props.className} ${

View File

@ -8,6 +8,7 @@ declare global {
PROXY_URL?: string; PROXY_URL?: string;
VERCEL?: string; VERCEL?: string;
HIDE_USER_API_KEY?: string; // disable user's api key input 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, proxyUrl: process.env.PROXY_URL,
isVercel: !!process.env.VERCEL, isVercel: !!process.env.VERCEL,
hideUserApiKey: !!process.env.HIDE_USER_API_KEY, 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 NARROW_SIDEBAR_WIDTH = 100;
export const ACCESS_CODE_PREFIX = "ak-"; 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: { modelConfig: {
model: "gpt-4", model: "gpt-4",
temperature: 1, temperature: 0.5,
max_tokens: 2000, max_tokens: 2000,
presence_penalty: 0, presence_penalty: 0,
sendMemory: true, sendMemory: true,

View File

@ -2,6 +2,7 @@ import { create } from "zustand";
import { persist } from "zustand/middleware"; import { persist } from "zustand/middleware";
import { StoreKey } from "../constant"; import { StoreKey } from "../constant";
import { BOT_HELLO } from "./chat"; import { BOT_HELLO } from "./chat";
import { ALL_MODELS } from "./config";
export interface AccessControlStore { export interface AccessControlStore {
accessCode: string; accessCode: string;
@ -60,6 +61,14 @@ export const useAccessStore = create<AccessControlStore>()(
console.log("[Config] got config from server", res); console.log("[Config] got config from server", res);
set(() => ({ ...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) { if ((res as any).botHello) {
BOT_HELLO.content = (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(); const sessions = get().sessions.slice();
sessions.splice(index, 1); sessions.splice(index, 1);
const currentIndex = get().currentSessionIndex;
let nextIndex = Math.min( let nextIndex = Math.min(
get().currentSessionIndex, currentIndex - Number(index < currentIndex),
sessions.length - 1, sessions.length - 1,
); );
@ -251,9 +252,20 @@ export const useChatStore = create<ChatStore>()(
model: modelConfig.model, model: modelConfig.model,
}); });
const systemInfo = createMessage({
role: "system",
content: `IMPRTANT: You are a virtual assistant powered by the ${
modelConfig.model
} model, now time is ${new Date().toLocaleString()}}`,
id: botMessage.id! + 1,
});
// get recent messages // get recent messages
const systemMessages = [systemInfo];
const recentMessages = get().getMessagesWithMemory(); const recentMessages = get().getMessagesWithMemory();
const sendMessages = recentMessages.concat(userMessage); const sendMessages = systemMessages.concat(
recentMessages.concat(userMessage),
);
const sessionIndex = get().currentSessionIndex; const sessionIndex = get().currentSessionIndex;
const messageIndex = get().currentSession().messages.length + 1; const messageIndex = get().currentSession().messages.length + 1;

View File

@ -76,6 +76,26 @@ export const ALL_MODELS = [
name: "gpt-3.5-turbo-0301", name: "gpt-3.5-turbo-0301",
available: true, 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; ] as const;
export type ModelType = (typeof ALL_MODELS)[number]["name"]; 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.style.width = width + "px";
measureDom.innerText = dom.value.trim().length > 0 ? dom.value : "1"; 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 height = parseFloat(window.getComputedStyle(measureDom).height);
const singleLineHeight = parseFloat( const singleLineHeight = parseFloat(
window.getComputedStyle(singleLineDom).height, window.getComputedStyle(singleLineDom).height,
); );
const rows = Math.round(height / singleLineHeight) + lineWrapCount; const rows = Math.round(height / singleLineHeight) + emptyLineWrap;
return rows; return rows;
} }