merge上游更新,升级为2.0版本

This commit is contained in:
dgqyushen
2023-04-28 17:39:46 +08:00
64 changed files with 2615 additions and 674 deletions

View File

@@ -1,5 +1,6 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { StoreKey } from "../constant";
export interface AccessControlStore {
accessCode: string;
@@ -14,8 +15,6 @@ export interface AccessControlStore {
fetch: () => void;
}
export const ACCESS_KEY = "access-control";
let fetchState = 0; // 0 not fetch, 1 fetching, 2 done
export const useAccessStore = create<AccessControlStore>()(
@@ -62,7 +61,7 @@ export const useAccessStore = create<AccessControlStore>()(
},
}),
{
name: ACCESS_KEY,
name: StoreKey.Access,
version: 1,
},
),

View File

@@ -11,7 +11,9 @@ import { isMobileScreen, trimTopic } from "../utils";
import Locale from "../locales";
import { showToast } from "../components/ui-lib";
import { ModelType, useAppConfig } from "./config";
import { DEFAULT_CONFIG, ModelConfig, ModelType, useAppConfig } from "./config";
import { createEmptyMask, Mask } from "./mask";
import { StoreKey } from "../constant";
export type Message = ChatCompletionResponseMessage & {
date: string;
@@ -41,50 +43,50 @@ export interface ChatStat {
export interface ChatSession {
id: number;
topic: string;
sendMemory: boolean;
memoryPrompt: string;
context: Message[];
messages: Message[];
stat: ChatStat;
lastUpdate: string;
lastUpdate: number;
lastSummarizeIndex: number;
mask: Mask;
}
const DEFAULT_TOPIC = Locale.Store.DefaultTopic;
export const DEFAULT_TOPIC = Locale.Store.DefaultTopic;
export const BOT_HELLO: Message = createMessage({
role: "assistant",
content: Locale.Store.BotHello,
});
function createEmptySession(): ChatSession {
const createDate = new Date().toLocaleString();
return {
id: Date.now(),
id: Date.now() + Math.random(),
topic: DEFAULT_TOPIC,
sendMemory: true,
memoryPrompt: "",
context: [],
messages: [],
stat: {
tokenCount: 0,
wordCount: 0,
charCount: 0,
},
lastUpdate: createDate,
lastUpdate: Date.now(),
lastSummarizeIndex: 0,
mask: createEmptyMask(),
};
}
interface ChatStore {
sessions: ChatSession[];
currentSessionIndex: number;
globalId: number;
clearSessions: () => void;
removeSession: (index: number) => void;
moveSession: (from: number, to: number) => void;
selectSession: (index: number) => void;
newSession: () => void;
newSession: (mask?: Mask) => void;
deleteSession: (index?: number) => void;
currentSession: () => ChatSession;
onNewMessage: (message: Message) => void;
@@ -108,13 +110,12 @@ function countMessages(msgs: Message[]) {
return msgs.reduce((pre, cur) => pre + cur.content.length, 0);
}
const LOCAL_KEY = "chat-next-web-store";
export const useChatStore = create<ChatStore>()(
persist(
(set, get) => ({
sessions: [createEmptySession()],
currentSessionIndex: 0,
globalId: 0,
clearSessions() {
set(() => ({
@@ -179,10 +180,20 @@ export const useChatStore = create<ChatStore>()(
});
},
newSession() {
newSession(mask) {
const session = createEmptySession();
set(() => ({ globalId: get().globalId + 1 }));
session.id = get().globalId;
if (mask) {
session.mask = { ...mask };
session.topic = mask.name;
}
set((state) => ({
currentSessionIndex: 0,
sessions: [createEmptySession()].concat(state.sessions),
sessions: [session].concat(state.sessions),
}));
},
@@ -229,7 +240,7 @@ export const useChatStore = create<ChatStore>()(
onNewMessage(message) {
get().updateCurrentSession((session) => {
session.lastUpdate = new Date().toLocaleString();
session.lastUpdate = Date.now();
});
get().updateStat(message);
get().summarizeSession();
@@ -308,7 +319,10 @@ export const useChatStore = create<ChatStore>()(
return {
role: "system",
content: Locale.Store.Prompt.History(session.memoryPrompt),
content:
session.memoryPrompt.length > 0
? Locale.Store.Prompt.History(session.memoryPrompt)
: "",
date: "",
} as Message;
},
@@ -319,11 +333,11 @@ export const useChatStore = create<ChatStore>()(
const messages = session.messages.filter((msg) => !msg.isError);
const n = messages.length;
const context = session.context.slice();
const context = session.mask.context.slice();
// long term memory
if (
session.sendMemory &&
session.mask.modelConfig.sendMemory &&
session.memoryPrompt &&
session.memoryPrompt.length > 0
) {
@@ -334,14 +348,14 @@ export const useChatStore = create<ChatStore>()(
// get short term and unmemoried long term memory
const shortTermMemoryMessageIndex = Math.max(
0,
n - config.historyMessageCount,
n - config.modelConfig.historyMessageCount,
);
const longTermMemoryMessageIndex = session.lastSummarizeIndex;
const oldestIndex = Math.max(
shortTermMemoryMessageIndex,
longTermMemoryMessageIndex,
);
const threshold = config.compressMessageLengthThreshold;
const threshold = config.modelConfig.compressMessageLengthThreshold;
// get recent messages as many as possible
const reversedRecentMessages = [];
@@ -410,7 +424,7 @@ export const useChatStore = create<ChatStore>()(
if (historyMsgLength > config?.modelConfig?.max_tokens ?? 4000) {
const n = toBeSummarizedMsgs.length;
toBeSummarizedMsgs = toBeSummarizedMsgs.slice(
Math.max(0, n - config.historyMessageCount),
Math.max(0, n - config.modelConfig.historyMessageCount),
);
}
@@ -423,12 +437,13 @@ export const useChatStore = create<ChatStore>()(
"[Chat History] ",
toBeSummarizedMsgs,
historyMsgLength,
config.compressMessageLengthThreshold,
config.modelConfig.compressMessageLengthThreshold,
);
if (
historyMsgLength > config.compressMessageLengthThreshold &&
session.sendMemory
historyMsgLength >
config.modelConfig.compressMessageLengthThreshold &&
session.mask.modelConfig.sendMemory
) {
requestChatStream(
toBeSummarizedMsgs.concat({
@@ -469,27 +484,34 @@ export const useChatStore = create<ChatStore>()(
},
clearAllData() {
if (confirm(Locale.Store.ConfirmClearAll)) {
localStorage.clear();
location.reload();
}
localStorage.clear();
location.reload();
},
}),
{
name: LOCAL_KEY,
version: 1.2,
name: StoreKey.Chat,
version: 2,
migrate(persistedState, version) {
const state = persistedState as ChatStore;
const state = persistedState as any;
const newState = JSON.parse(JSON.stringify(state)) as ChatStore;
if (version === 1) {
state.sessions.forEach((s) => (s.context = []));
if (version < 2) {
newState.globalId = 0;
newState.sessions = [];
const oldSessions = state.sessions;
for (const oldSession of oldSessions) {
const newSession = createEmptySession();
newSession.topic = oldSession.topic;
newSession.messages = [...oldSession.messages];
newSession.mask.modelConfig.sendMemory = true;
newSession.mask.modelConfig.historyMessageCount = 4;
newSession.mask.modelConfig.compressMessageLengthThreshold = 1000;
newState.sessions.push(newSession);
}
}
if (version < 1.2) {
state.sessions.forEach((s) => (s.sendMemory = true));
}
return state;
return newState;
},
},
),

View File

@@ -1,5 +1,6 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { StoreKey } from "../constant";
export enum SubmitKey {
Enter = "Enter",
@@ -15,9 +16,7 @@ export enum Theme {
Light = "light",
}
const DEFAULT_CONFIG = {
historyMessageCount: 4,
compressMessageLengthThreshold: 1000,
export const DEFAULT_CONFIG = {
sendBotMessages: true as boolean,
submitKey: SubmitKey.CtrlEnter as SubmitKey,
avatar: "1f603",
@@ -29,11 +28,16 @@ const DEFAULT_CONFIG = {
disablePromptHint: false,
dontShowMaskSplashScreen: false, // dont show splash screen when create chat
modelConfig: {
model: "gpt-3.5-turbo" as ModelType,
temperature: 1,
max_tokens: 2000,
presence_penalty: 0,
sendMemory: true,
historyMessageCount: 4,
compressMessageLengthThreshold: 1000,
},
};
@@ -107,12 +111,10 @@ export const ModalConfigValidator = {
return limitNumber(x, -2, 2, 0);
},
temperature(x: number) {
return limitNumber(x, 0, 2, 1);
return limitNumber(x, 0, 1, 1);
},
};
const CONFIG_KEY = "app-config";
export const useAppConfig = create<ChatConfigStore>()(
persist(
(set, get) => ({
@@ -129,7 +131,19 @@ export const useAppConfig = create<ChatConfigStore>()(
},
}),
{
name: CONFIG_KEY,
name: StoreKey.Config,
version: 2,
migrate(persistedState, version) {
if (version === 2) return persistedState as any;
const state = persistedState as ChatConfig;
state.modelConfig.sendMemory = true;
state.modelConfig.historyMessageCount = 4;
state.modelConfig.compressMessageLengthThreshold = 1000;
state.dontShowMaskSplashScreen = false;
return state;
},
},
),
);

View File

@@ -1,4 +1,4 @@
export * from "./app";
export * from "./chat";
export * from "./update";
export * from "./access";
export * from "./config";

99
app/store/mask.ts Normal file
View File

@@ -0,0 +1,99 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { BUILTIN_MASKS } from "../masks";
import { getLang, Lang } from "../locales";
import { DEFAULT_TOPIC, Message } from "./chat";
import { ModelConfig, ModelType, useAppConfig } from "./config";
import { StoreKey } from "../constant";
export type Mask = {
id: number;
avatar: string;
name: string;
context: Message[];
modelConfig: ModelConfig;
lang: Lang;
builtin: boolean;
};
export const DEFAULT_MASK_STATE = {
masks: {} as Record<number, Mask>,
globalMaskId: 0,
};
export type MaskState = typeof DEFAULT_MASK_STATE;
type MaskStore = MaskState & {
create: (mask?: Partial<Mask>) => Mask;
update: (id: number, updater: (mask: Mask) => void) => void;
delete: (id: number) => void;
search: (text: string) => Mask[];
get: (id?: number) => Mask | null;
getAll: () => Mask[];
};
export const DEFAULT_MASK_ID = 1145141919810;
export const DEFAULT_MASK_AVATAR = "gpt-bot";
export const createEmptyMask = () =>
({
id: DEFAULT_MASK_ID,
avatar: DEFAULT_MASK_AVATAR,
name: DEFAULT_TOPIC,
context: [],
modelConfig: { ...useAppConfig.getState().modelConfig },
lang: getLang(),
builtin: false,
} as Mask);
export const useMaskStore = create<MaskStore>()(
persist(
(set, get) => ({
...DEFAULT_MASK_STATE,
create(mask) {
set(() => ({ globalMaskId: get().globalMaskId + 1 }));
const id = get().globalMaskId;
const masks = get().masks;
masks[id] = {
...createEmptyMask(),
...mask,
id,
};
set(() => ({ masks }));
return masks[id];
},
update(id, updater) {
const masks = get().masks;
const mask = masks[id];
if (!mask) return;
const updateMask = { ...mask };
updater(updateMask);
masks[id] = updateMask;
set(() => ({ masks }));
},
delete(id) {
const masks = get().masks;
delete masks[id];
set(() => ({ masks }));
},
get(id) {
return get().masks[id ?? 1145141919810];
},
getAll() {
const userMasks = Object.values(get().masks).sort(
(a, b) => b.id - a.id,
);
return userMasks.concat(BUILTIN_MASKS);
},
search(text) {
return Object.values(get().masks);
},
}),
{
name: StoreKey.Mask,
version: 2,
},
),
);

View File

@@ -2,6 +2,7 @@ import { create } from "zustand";
import { persist } from "zustand/middleware";
import Fuse from "fuse.js";
import { getLang } from "../locales";
import { StoreKey } from "../constant";
export interface Prompt {
id?: number;
@@ -23,8 +24,6 @@ export interface PromptStore {
updateUserPrompts: (id: number, updater: (prompt: Prompt) => void) => void;
}
export const PROMPT_KEY = "prompt-store";
export const SearchService = {
ready: false,
builtinEngine: new Fuse<Prompt>([], { keys: ["title"] }),
@@ -123,7 +122,7 @@ export const usePromptStore = create<PromptStore>()(
},
}),
{
name: PROMPT_KEY,
name: StoreKey.Prompt,
version: 1,
onRehydrateStorage(state) {
const PROMPT_URL = "./prompts.json";

View File

@@ -1,6 +1,6 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { FETCH_COMMIT_URL, FETCH_TAG_URL } from "../constant";
import { FETCH_COMMIT_URL, FETCH_TAG_URL, StoreKey } from "../constant";
import { requestUsage } from "../requests";
export interface UpdateStore {
@@ -16,8 +16,6 @@ export interface UpdateStore {
updateUsage: (force?: boolean) => Promise<void>;
}
export const UPDATE_KEY = "chat-update";
function queryMeta(key: string, defaultValue?: string): string {
let ret: string;
if (document) {
@@ -84,7 +82,7 @@ export const useUpdateStore = create<UpdateStore>()(
},
}),
{
name: UPDATE_KEY,
name: StoreKey.Update,
version: 1,
},
),