forked from XiaoMo/ChatGPT-Next-Web
commit
5e544891aa
@ -67,7 +67,10 @@ export function ChatItem(props: {
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className={styles["chat-item-delete"]} onClick={props.onDelete}>
|
||||
<div
|
||||
className={styles["chat-item-delete"]}
|
||||
onClickCapture={props.onDelete}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</div>
|
||||
</div>
|
||||
@ -77,14 +80,14 @@ export function ChatItem(props: {
|
||||
}
|
||||
|
||||
export function ChatList(props: { narrow?: boolean }) {
|
||||
const [sessions, selectedIndex, selectSession, removeSession, moveSession] =
|
||||
useChatStore((state) => [
|
||||
const [sessions, selectedIndex, selectSession, moveSession] = useChatStore(
|
||||
(state) => [
|
||||
state.sessions,
|
||||
state.currentSessionIndex,
|
||||
state.selectSession,
|
||||
state.removeSession,
|
||||
state.moveSession,
|
||||
]);
|
||||
],
|
||||
);
|
||||
const chatStore = useChatStore();
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
@ -391,7 +391,7 @@ export function Chat() {
|
||||
const onPromptSelect = (prompt: Prompt) => {
|
||||
setPromptHints([]);
|
||||
inputRef.current?.focus();
|
||||
setUserInput(prompt.content);
|
||||
setTimeout(() => setUserInput(prompt.content), 60);
|
||||
};
|
||||
|
||||
// auto grow input
|
||||
|
@ -296,7 +296,10 @@ export function MaskPage() {
|
||||
icon={<AddIcon />}
|
||||
text={Locale.Mask.Page.Create}
|
||||
bordered
|
||||
onClick={() => maskStore.create()}
|
||||
onClick={() => {
|
||||
const createdMask = maskStore.create();
|
||||
setEditingMaskId(createdMask.id);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -138,7 +138,11 @@ export function SideBar(props: { className?: string }) {
|
||||
<div className={styles["sidebar-action"] + " " + styles.mobile}>
|
||||
<IconButton
|
||||
icon={<CloseIcon />}
|
||||
onClick={chatStore.deleteSession}
|
||||
onClick={() => {
|
||||
if (confirm(Locale.Home.DeleteChat)) {
|
||||
chatStore.deleteSession(chatStore.currentSessionIndex);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles["sidebar-action"]}>
|
||||
|
@ -158,6 +158,7 @@ export type ToastProps = {
|
||||
text: string;
|
||||
onClick: () => void;
|
||||
};
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
export function Toast(props: ToastProps) {
|
||||
@ -167,7 +168,10 @@ export function Toast(props: ToastProps) {
|
||||
<span>{props.content}</span>
|
||||
{props.action && (
|
||||
<button
|
||||
onClick={props.action.onClick}
|
||||
onClick={() => {
|
||||
props.action?.onClick?.();
|
||||
props.onClose?.();
|
||||
}}
|
||||
className={styles["toast-action"]}
|
||||
>
|
||||
{props.action.text}
|
||||
@ -201,7 +205,7 @@ export function showToast(
|
||||
close();
|
||||
}, delay);
|
||||
|
||||
root.render(<Toast content={content} action={action} />);
|
||||
root.render(<Toast content={content} action={action} onClose={close} />);
|
||||
}
|
||||
|
||||
export type InputProps = React.HTMLProps<HTMLTextAreaElement> & {
|
||||
|
@ -14,9 +14,8 @@ const TIME_OUT_MS = 60000;
|
||||
const makeRequestParam = (
|
||||
messages: Message[],
|
||||
options?: {
|
||||
filterBot?: boolean;
|
||||
stream?: boolean;
|
||||
model?: ModelType;
|
||||
overrideModel?: ModelType;
|
||||
},
|
||||
): ChatRequest => {
|
||||
let sendMessages = messages.map((v) => ({
|
||||
@ -24,18 +23,14 @@ const makeRequestParam = (
|
||||
content: v.content,
|
||||
}));
|
||||
|
||||
if (options?.filterBot) {
|
||||
sendMessages = sendMessages.filter((m) => m.role !== "assistant");
|
||||
}
|
||||
|
||||
const modelConfig = {
|
||||
...useAppConfig.getState().modelConfig,
|
||||
...useChatStore.getState().currentSession().mask.modelConfig,
|
||||
};
|
||||
|
||||
// override model config
|
||||
if (options?.model) {
|
||||
modelConfig.model = options.model;
|
||||
if (options?.overrideModel) {
|
||||
modelConfig.model = options.overrideModel;
|
||||
}
|
||||
|
||||
return {
|
||||
@ -82,8 +77,7 @@ export async function requestChat(
|
||||
},
|
||||
) {
|
||||
const req: ChatRequest = makeRequestParam(messages, {
|
||||
filterBot: true,
|
||||
model: options?.model,
|
||||
overrideModel: options?.model,
|
||||
});
|
||||
|
||||
const res = await requestOpenaiClient("v1/chat/completions")(req);
|
||||
@ -102,11 +96,11 @@ export async function requestUsage() {
|
||||
.getDate()
|
||||
.toString()
|
||||
.padStart(2, "0")}`;
|
||||
const ONE_DAY = 2 * 24 * 60 * 60 * 1000;
|
||||
const now = new Date(Date.now() + ONE_DAY);
|
||||
const ONE_DAY = 1 * 24 * 60 * 60 * 1000;
|
||||
const now = new Date();
|
||||
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
const startDate = formatDate(startOfMonth);
|
||||
const endDate = formatDate(now);
|
||||
const endDate = formatDate(new Date(Date.now() + ONE_DAY));
|
||||
|
||||
const [used, subs] = await Promise.all([
|
||||
requestOpenaiClient(
|
||||
@ -149,9 +143,8 @@ export async function requestUsage() {
|
||||
export async function requestChatStream(
|
||||
messages: Message[],
|
||||
options?: {
|
||||
filterBot?: boolean;
|
||||
modelConfig?: ModelConfig;
|
||||
model?: ModelType;
|
||||
overrideModel?: ModelType;
|
||||
onMessage: (message: string, done: boolean) => void;
|
||||
onError: (error: Error, statusCode?: number) => void;
|
||||
onController?: (controller: AbortController) => void;
|
||||
@ -159,8 +152,7 @@ export async function requestChatStream(
|
||||
) {
|
||||
const req = makeRequestParam(messages, {
|
||||
stream: true,
|
||||
filterBot: options?.filterBot,
|
||||
model: options?.model,
|
||||
overrideModel: options?.overrideModel,
|
||||
});
|
||||
|
||||
console.log("[Request] ", req);
|
||||
|
@ -83,11 +83,10 @@ interface ChatStore {
|
||||
currentSessionIndex: number;
|
||||
globalId: number;
|
||||
clearSessions: () => void;
|
||||
removeSession: (index: number) => void;
|
||||
moveSession: (from: number, to: number) => void;
|
||||
selectSession: (index: number) => void;
|
||||
newSession: (mask?: Mask) => void;
|
||||
deleteSession: (index?: number) => void;
|
||||
deleteSession: (index: number) => void;
|
||||
currentSession: () => ChatSession;
|
||||
onNewMessage: (message: Message) => void;
|
||||
onUserInput: (content: string) => Promise<void>;
|
||||
@ -130,31 +129,6 @@ export const useChatStore = create<ChatStore>()(
|
||||
});
|
||||
},
|
||||
|
||||
removeSession(index: number) {
|
||||
set((state) => {
|
||||
let nextIndex = state.currentSessionIndex;
|
||||
const sessions = state.sessions;
|
||||
|
||||
if (sessions.length === 1) {
|
||||
return {
|
||||
currentSessionIndex: 0,
|
||||
sessions: [createEmptySession()],
|
||||
};
|
||||
}
|
||||
|
||||
sessions.splice(index, 1);
|
||||
|
||||
if (nextIndex === index) {
|
||||
nextIndex -= 1;
|
||||
}
|
||||
|
||||
return {
|
||||
currentSessionIndex: nextIndex,
|
||||
sessions,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
moveSession(from: number, to: number) {
|
||||
set((state) => {
|
||||
const { sessions, currentSessionIndex: oldIndex } = state;
|
||||
@ -197,31 +171,46 @@ export const useChatStore = create<ChatStore>()(
|
||||
}));
|
||||
},
|
||||
|
||||
deleteSession(i?: number) {
|
||||
const deletedSession = get().currentSession();
|
||||
const index = i ?? get().currentSessionIndex;
|
||||
const isLastSession = get().sessions.length === 1;
|
||||
if (!isMobileScreen() || confirm(Locale.Home.DeleteChat)) {
|
||||
get().removeSession(index);
|
||||
deleteSession(index) {
|
||||
const deletingLastSession = get().sessions.length === 1;
|
||||
const deletedSession = get().sessions.at(index);
|
||||
|
||||
showToast(
|
||||
Locale.Home.DeleteToast,
|
||||
{
|
||||
text: Locale.Home.Revert,
|
||||
onClick() {
|
||||
set((state) => ({
|
||||
sessions: state.sessions
|
||||
.slice(0, index)
|
||||
.concat([deletedSession])
|
||||
.concat(
|
||||
state.sessions.slice(index + Number(isLastSession)),
|
||||
),
|
||||
}));
|
||||
},
|
||||
},
|
||||
5000,
|
||||
);
|
||||
if (!deletedSession) return;
|
||||
|
||||
const sessions = get().sessions.slice();
|
||||
sessions.splice(index, 1);
|
||||
|
||||
let nextIndex = Math.min(
|
||||
get().currentSessionIndex,
|
||||
sessions.length - 1,
|
||||
);
|
||||
|
||||
if (deletingLastSession) {
|
||||
nextIndex = 0;
|
||||
sessions.push(createEmptySession());
|
||||
}
|
||||
|
||||
// for undo delete action
|
||||
const restoreState = {
|
||||
currentSessionIndex: get().currentSessionIndex,
|
||||
sessions: get().sessions.slice(),
|
||||
};
|
||||
|
||||
set(() => ({
|
||||
currentSessionIndex: nextIndex,
|
||||
sessions,
|
||||
}));
|
||||
|
||||
showToast(
|
||||
Locale.Home.DeleteToast,
|
||||
{
|
||||
text: Locale.Home.Revert,
|
||||
onClick() {
|
||||
set(() => restoreState);
|
||||
},
|
||||
},
|
||||
5000,
|
||||
);
|
||||
},
|
||||
|
||||
currentSession() {
|
||||
@ -247,6 +236,9 @@ export const useChatStore = create<ChatStore>()(
|
||||
},
|
||||
|
||||
async onUserInput(content) {
|
||||
const session = get().currentSession();
|
||||
const modelConfig = session.mask.modelConfig;
|
||||
|
||||
const userMessage: Message = createMessage({
|
||||
role: "user",
|
||||
content,
|
||||
@ -256,7 +248,7 @@ export const useChatStore = create<ChatStore>()(
|
||||
role: "assistant",
|
||||
streaming: true,
|
||||
id: userMessage.id! + 1,
|
||||
model: useAppConfig.getState().modelConfig.model,
|
||||
model: modelConfig.model,
|
||||
});
|
||||
|
||||
// get recent messages
|
||||
@ -290,14 +282,16 @@ export const useChatStore = create<ChatStore>()(
|
||||
}
|
||||
},
|
||||
onError(error, statusCode) {
|
||||
const isAborted = error.message.includes("aborted");
|
||||
if (statusCode === 401) {
|
||||
botMessage.content = Locale.Error.Unauthorized;
|
||||
} else if (!error.message.includes("aborted")) {
|
||||
} else if (!isAborted) {
|
||||
botMessage.content += "\n\n" + Locale.Store.Error;
|
||||
}
|
||||
botMessage.streaming = false;
|
||||
userMessage.isError = true;
|
||||
botMessage.isError = true;
|
||||
userMessage.isError = !isAborted;
|
||||
botMessage.isError = !isAborted;
|
||||
|
||||
set(() => ({}));
|
||||
ControllerPool.remove(sessionIndex, botMessage.id ?? messageIndex);
|
||||
},
|
||||
@ -309,8 +303,7 @@ export const useChatStore = create<ChatStore>()(
|
||||
controller,
|
||||
);
|
||||
},
|
||||
filterBot: !useAppConfig.getState().sendBotMessages,
|
||||
modelConfig: useAppConfig.getState().modelConfig,
|
||||
modelConfig: { ...modelConfig },
|
||||
});
|
||||
},
|
||||
|
||||
@ -329,7 +322,7 @@ export const useChatStore = create<ChatStore>()(
|
||||
|
||||
getMessagesWithMemory() {
|
||||
const session = get().currentSession();
|
||||
const config = useAppConfig.getState();
|
||||
const modelConfig = session.mask.modelConfig;
|
||||
const messages = session.messages.filter((msg) => !msg.isError);
|
||||
const n = messages.length;
|
||||
|
||||
@ -337,7 +330,7 @@ export const useChatStore = create<ChatStore>()(
|
||||
|
||||
// long term memory
|
||||
if (
|
||||
session.mask.modelConfig.sendMemory &&
|
||||
modelConfig.sendMemory &&
|
||||
session.memoryPrompt &&
|
||||
session.memoryPrompt.length > 0
|
||||
) {
|
||||
@ -348,14 +341,14 @@ export const useChatStore = create<ChatStore>()(
|
||||
// get short term and unmemoried long term memory
|
||||
const shortTermMemoryMessageIndex = Math.max(
|
||||
0,
|
||||
n - config.modelConfig.historyMessageCount,
|
||||
n - modelConfig.historyMessageCount,
|
||||
);
|
||||
const longTermMemoryMessageIndex = session.lastSummarizeIndex;
|
||||
const oldestIndex = Math.max(
|
||||
shortTermMemoryMessageIndex,
|
||||
longTermMemoryMessageIndex,
|
||||
);
|
||||
const threshold = config.modelConfig.compressMessageLengthThreshold;
|
||||
const threshold = modelConfig.compressMessageLengthThreshold;
|
||||
|
||||
// get recent messages as many as possible
|
||||
const reversedRecentMessages = [];
|
||||
@ -414,17 +407,17 @@ export const useChatStore = create<ChatStore>()(
|
||||
});
|
||||
}
|
||||
|
||||
const config = useAppConfig.getState();
|
||||
const modelConfig = session.mask.modelConfig;
|
||||
let toBeSummarizedMsgs = session.messages.slice(
|
||||
session.lastSummarizeIndex,
|
||||
);
|
||||
|
||||
const historyMsgLength = countMessages(toBeSummarizedMsgs);
|
||||
|
||||
if (historyMsgLength > config?.modelConfig?.max_tokens ?? 4000) {
|
||||
if (historyMsgLength > modelConfig?.max_tokens ?? 4000) {
|
||||
const n = toBeSummarizedMsgs.length;
|
||||
toBeSummarizedMsgs = toBeSummarizedMsgs.slice(
|
||||
Math.max(0, n - config.modelConfig.historyMessageCount),
|
||||
Math.max(0, n - modelConfig.historyMessageCount),
|
||||
);
|
||||
}
|
||||
|
||||
@ -437,12 +430,11 @@ export const useChatStore = create<ChatStore>()(
|
||||
"[Chat History] ",
|
||||
toBeSummarizedMsgs,
|
||||
historyMsgLength,
|
||||
config.modelConfig.compressMessageLengthThreshold,
|
||||
modelConfig.compressMessageLengthThreshold,
|
||||
);
|
||||
|
||||
if (
|
||||
historyMsgLength >
|
||||
config.modelConfig.compressMessageLengthThreshold &&
|
||||
historyMsgLength > modelConfig.compressMessageLengthThreshold &&
|
||||
session.mask.modelConfig.sendMemory
|
||||
) {
|
||||
requestChatStream(
|
||||
@ -452,8 +444,7 @@ export const useChatStore = create<ChatStore>()(
|
||||
date: "",
|
||||
}),
|
||||
{
|
||||
filterBot: false,
|
||||
model: "gpt-3.5-turbo",
|
||||
overrideModel: "gpt-3.5-turbo",
|
||||
onMessage(message, done) {
|
||||
session.memoryPrompt = message;
|
||||
if (done) {
|
||||
|
@ -17,7 +17,6 @@ export enum Theme {
|
||||
}
|
||||
|
||||
export const DEFAULT_CONFIG = {
|
||||
sendBotMessages: true as boolean,
|
||||
submitKey: SubmitKey.CtrlEnter as SubmitKey,
|
||||
avatar: "1f603",
|
||||
fontSize: 14,
|
||||
|
Loading…
Reference in New Issue
Block a user