forked from XiaoMo/ChatGPT-Next-Web
feat: close #1415 clear context button
This commit is contained in:
parent
c2b36cdffa
commit
a19d238483
@ -107,3 +107,68 @@
|
|||||||
user-select: text;
|
user-select: text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clear-context {
|
||||||
|
margin: 20px 0 0 0;
|
||||||
|
padding: 4px 0;
|
||||||
|
|
||||||
|
border-top: var(--border-in-light);
|
||||||
|
border-bottom: var(--border-in-light);
|
||||||
|
box-shadow: var(--card-shadow) inset;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
opacity: 0.5;
|
||||||
|
color: var(--black);
|
||||||
|
transition: all ease 0.3s;
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
$linear: linear-gradient(
|
||||||
|
to right,
|
||||||
|
rgba(0, 0, 0, 0),
|
||||||
|
rgba(0, 0, 0, 1),
|
||||||
|
rgba(0, 0, 0, 0)
|
||||||
|
);
|
||||||
|
mask-image: $linear;
|
||||||
|
|
||||||
|
@mixin show {
|
||||||
|
transform: translateY(0);
|
||||||
|
position: relative;
|
||||||
|
transition: all ease 0.3s;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin hide {
|
||||||
|
transform: translateY(-50%);
|
||||||
|
position: absolute;
|
||||||
|
transition: all ease 0.1s;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tips {
|
||||||
|
@include show;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-revert-btn {
|
||||||
|
color: var(--primary);
|
||||||
|
@include hide;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
border-color: var(--primary);
|
||||||
|
|
||||||
|
.clear-context-tips {
|
||||||
|
@include hide;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-context-revert-btn {
|
||||||
|
@include show;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -14,6 +14,8 @@ import MaskIcon from "../icons/mask.svg";
|
|||||||
import MaxIcon from "../icons/max.svg";
|
import MaxIcon from "../icons/max.svg";
|
||||||
import MinIcon from "../icons/min.svg";
|
import MinIcon from "../icons/min.svg";
|
||||||
import ResetIcon from "../icons/reload.svg";
|
import ResetIcon from "../icons/reload.svg";
|
||||||
|
import BreakIcon from "../icons/break.svg";
|
||||||
|
import SettingsIcon from "../icons/chat-settings.svg";
|
||||||
|
|
||||||
import LightIcon from "../icons/light.svg";
|
import LightIcon from "../icons/light.svg";
|
||||||
import DarkIcon from "../icons/dark.svg";
|
import DarkIcon from "../icons/dark.svg";
|
||||||
@ -51,7 +53,7 @@ import { IconButton } from "./button";
|
|||||||
import styles from "./home.module.scss";
|
import styles from "./home.module.scss";
|
||||||
import chatStyle from "./chat.module.scss";
|
import chatStyle from "./chat.module.scss";
|
||||||
|
|
||||||
import { ListItem, Modal, showModal } from "./ui-lib";
|
import { ListItem, Modal, showModal, showToast } from "./ui-lib";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import { LAST_INPUT_KEY, Path, REQUEST_TIMEOUT_MS } from "../constant";
|
import { LAST_INPUT_KEY, Path, REQUEST_TIMEOUT_MS } from "../constant";
|
||||||
import { Avatar } from "./emoji";
|
import { Avatar } from "./emoji";
|
||||||
@ -289,6 +291,24 @@ export function PromptHints(props: {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ClearContextDivider() {
|
||||||
|
const chatStore = useChatStore();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={chatStyle["clear-context"]}
|
||||||
|
onClick={() =>
|
||||||
|
chatStore.updateCurrentSession(
|
||||||
|
(session) => (session.clearContextIndex = -1),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={chatStyle["clear-context-tips"]}>上下文已清除</div>
|
||||||
|
<div className={chatStyle["clear-context-revert-btn"]}>取消清除</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function useScrollToBottom() {
|
function useScrollToBottom() {
|
||||||
// for auto-scroll
|
// for auto-scroll
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
@ -321,6 +341,7 @@ export function ChatActions(props: {
|
|||||||
}) {
|
}) {
|
||||||
const config = useAppConfig();
|
const config = useAppConfig();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const chatStore = useChatStore();
|
||||||
|
|
||||||
// switch themes
|
// switch themes
|
||||||
const theme = config.theme;
|
const theme = config.theme;
|
||||||
@ -359,7 +380,7 @@ export function ChatActions(props: {
|
|||||||
className={`${chatStyle["chat-input-action"]} clickable`}
|
className={`${chatStyle["chat-input-action"]} clickable`}
|
||||||
onClick={props.showPromptModal}
|
onClick={props.showPromptModal}
|
||||||
>
|
>
|
||||||
<BrainIcon />
|
<SettingsIcon />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -391,6 +412,22 @@ export function ChatActions(props: {
|
|||||||
>
|
>
|
||||||
<MaskIcon />
|
<MaskIcon />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`${chatStyle["chat-input-action"]} clickable`}
|
||||||
|
onClick={() => {
|
||||||
|
chatStore.updateCurrentSession((session) => {
|
||||||
|
if ((session.clearContextIndex ?? -1) > 0) {
|
||||||
|
session.clearContextIndex = -1;
|
||||||
|
} else {
|
||||||
|
session.clearContextIndex = session.messages.length;
|
||||||
|
session.memoryPrompt = ""; // will clear memory
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<BreakIcon />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -602,6 +639,12 @@ export function Chat() {
|
|||||||
context.push(copiedHello);
|
context.push(copiedHello);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear context index = context length + index in messages
|
||||||
|
const clearContextIndex =
|
||||||
|
(session.clearContextIndex ?? -1) >= 0
|
||||||
|
? session.clearContextIndex! + context.length
|
||||||
|
: -1;
|
||||||
|
|
||||||
// preview messages
|
// preview messages
|
||||||
const messages = context
|
const messages = context
|
||||||
.concat(session.messages as RenderMessage[])
|
.concat(session.messages as RenderMessage[])
|
||||||
@ -736,7 +779,10 @@ export function Chat() {
|
|||||||
!(message.preview || message.content.length === 0);
|
!(message.preview || message.content.length === 0);
|
||||||
const showTyping = message.preview || message.streaming;
|
const showTyping = message.preview || message.streaming;
|
||||||
|
|
||||||
|
const shouldShowClearContextDivider = i === clearContextIndex - 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className={
|
className={
|
||||||
@ -816,6 +862,8 @@ export function Chat() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{shouldShowClearContextDivider && <ClearContextDivider />}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
1
app/icons/break.svg
Normal file
1
app/icons/break.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><g opacity="1" transform="translate(0 0) rotate(0)"><g opacity="1" transform="translate(2 2) rotate(0)"><path id="路径 1" style="fill:#333333; opacity:1;" d="M12.2752,-0.27515c0.261,0.26101 0.3915,0.57606 0.3915,0.94515v10.66c0,0.36907 -0.1305,0.68413 -0.3915,0.9452c-0.26107,0.261 -0.57613,0.3915 -0.9452,0.3915h-10.66c-0.36909,0 -0.68415,-0.1305 -0.94515,-0.3915c-0.26101,-0.26107 -0.39151,-0.57613 -0.39151,-0.9452v-10.66c0,-0.3691 0.1305,-0.68415 0.39151,-0.94515c0.26101,-0.26101 0.57606,-0.39151 0.94515,-0.39151h10.66c0.36907,0 0.68413,0.1305 0.9452,0.39151zM0.66667,11.33c0,0.0022 0.00111,0.0033 0.00333,0.0033h10.66c0.0022,0 0.0033,-0.0011 0.0033,-0.0033v-10.66c0,-0.00222 -0.0011,-0.00333 -0.0033,-0.00333l-10.66,0c-0.00222,0 -0.00333,0.00111 -0.00333,0.00333z"></path><path id="路径 2" style="fill:#333333; opacity:1;" d="M8.47141,7.4714c-0.03095,0.03095 -0.06463,0.05859 -0.10103,0.08291c-0.0364,0.02432 -0.07482,0.04486 -0.11526,0.06161c-0.04044,0.01675 -0.08213,0.0294 -0.12506,0.03794c-0.04293,0.00854 -0.08629,0.01281 -0.13006,0.01281c-0.04377,0 -0.08713,-0.00427 -0.13006,-0.01281c-0.04293,-0.00854 -0.08462,-0.02119 -0.12506,-0.03794c-0.04045,-0.01675 -0.07887,-0.03729 -0.11526,-0.06161c-0.0364,-0.02432 -0.07007,-0.05196 -0.10102,-0.08291l-1.5286,-1.52859l-1.52859,1.52859c-0.06251,0.06251 -0.13461,0.11069 -0.21629,0.14452c-0.08167,0.03383 -0.16671,0.05075 -0.25512,0.05075c-0.08841,0 -0.17345,-0.01692 -0.25512,-0.05075c-0.08168,-0.03383 -0.15377,-0.08201 -0.21628,-0.14452l-1.5286,-1.52859l-1.52859,1.52859c-0.06251,0.06251 -0.13461,0.11069 -0.21628,0.14452c-0.08168,0.03383 -0.16672,0.05075 -0.25512,0.05075c-0.08841,0 -0.17345,-0.01692 -0.25512,-0.05075c-0.08168,-0.03383 -0.15377,-0.08201 -0.21628,-0.14452c-0.03095,-0.03095 -0.05859,-0.06463 -0.08291,-0.10102c-0.02432,-0.0364 -0.04485,-0.07482 -0.06161,-0.11526c-0.01675,-0.04044 -0.0294,-0.08213 -0.03794,-0.12506c-0.00854,-0.04293 -0.01281,-0.08629 -0.01281,-0.13006c0,-0.04377 0.00427,-0.08713 0.01281,-0.13006c0.00854,-0.04293 0.02119,-0.08462 0.03794,-0.12506c0.01675,-0.04045 0.03729,-0.07887 0.06161,-0.11526c0.02432,-0.0364 0.05196,-0.07008 0.08291,-0.10103l2,-2c0.06251,-0.06251 0.1346,-0.11068 0.21628,-0.14451c0.08167,-0.03383 0.16671,-0.05075 0.25512,-0.05075c0.08841,0 0.17345,0.01692 0.25512,0.05075c0.08168,0.03383 0.15378,0.08201 0.21629,0.14452l1.52859,1.52859l1.5286,-1.52859c0.03095,-0.03095 0.06463,-0.05859 0.10102,-0.08291c0.03639,-0.02432 0.07481,-0.04486 0.11526,-0.06161c0.04044,-0.01675 0.08213,-0.0294 0.12506,-0.03794c0.04293,-0.00854 0.08629,-0.01281 0.13006,-0.01281c0.04377,0 0.08713,0.00427 0.13006,0.01281c0.04293,0.00854 0.08462,0.02119 0.12506,0.03794c0.04044,0.01675 0.07886,0.03729 0.11526,0.06161c0.0364,0.02432 0.07008,0.05196 0.10103,0.08291l1.52859,1.52859l1.5286,-1.52859c0.03095,-0.03095 0.06462,-0.05859 0.10102,-0.08291c0.03639,-0.02432 0.07481,-0.04486 0.11526,-0.06161c0.04044,-0.01675 0.08213,-0.0294 0.12506,-0.03794c0.04293,-0.00854 0.08629,-0.01281 0.13006,-0.01281c0.0438,0 0.08717,0.00427 0.1301,0.01281c0.04293,0.00854 0.0846,0.02119 0.125,0.03794c0.04047,0.01675 0.0789,0.03729 0.1153,0.06161c0.0364,0.02432 0.07007,0.05196 0.101,0.08291l2,2c0.03093,0.03095 0.05857,0.06462 0.0829,0.10102c0.02433,0.03639 0.04487,0.07481 0.0616,0.11526c0.01673,0.04044 0.0294,0.08213 0.038,0.12506c0.00853,0.04293 0.0128,0.08629 0.0128,0.13006c0,0.04377 -0.00427,0.08713 -0.0128,0.13006c-0.0086,0.04293 -0.02127,0.08462 -0.038,0.12506c-0.01673,0.04044 -0.03727,0.07886 -0.0616,0.11526c-0.02433,0.03639 -0.05197,0.07007 -0.0829,0.10102c-0.06253,0.06251 -0.13463,0.11069 -0.2163,0.14452c-0.08167,0.03383 -0.1667,0.05075 -0.2551,0.05075c-0.0884,0 -0.17343,-0.01692 -0.2551,-0.05075c-0.08167,-0.03383 -0.15377,-0.08201 -0.2163,-0.14452l-1.5286,-1.52859z"></path></g><g opacity="1" transform="translate(0 0) rotate(0)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ></g></g></g><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs></svg>
|
After Width: | Height: | Size: 4.1 KiB |
1
app/icons/chat-settings.svg
Normal file
1
app/icons/chat-settings.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 9.0 KiB |
@ -160,12 +160,11 @@ const cn = {
|
|||||||
BotHello: "有什么可以帮你的吗",
|
BotHello: "有什么可以帮你的吗",
|
||||||
Error: "出错了,稍后重试吧",
|
Error: "出错了,稍后重试吧",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) => "这是历史聊天总结作为前情提要:" + content,
|
||||||
"这是 ai 和用户的历史聊天总结作为前情提要:" + content,
|
|
||||||
Topic:
|
Topic:
|
||||||
"使用四到五个字直接返回这句话的简要主题,不要解释、不要标点、不要语气词、不要多余文本,如果没有主题,请直接返回“闲聊”",
|
"使用四到五个字直接返回这句话的简要主题,不要解释、不要标点、不要语气词、不要多余文本,如果没有主题,请直接返回“闲聊”",
|
||||||
Summarize:
|
Summarize:
|
||||||
"简要总结一下你和用户的对话,用作后续的上下文提示 prompt,控制在 200 字以内",
|
"简要总结一下对话内容,用作后续的上下文提示 prompt,控制在 200 字以内",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
@ -173,7 +172,7 @@ const cn = {
|
|||||||
Failed: "复制失败,请赋予剪切板权限",
|
Failed: "复制失败,请赋予剪切板权限",
|
||||||
},
|
},
|
||||||
Context: {
|
Context: {
|
||||||
Toast: (x: any) => `已设置 ${x} 条前置上下文`,
|
Toast: (x: any) => `包含 ${x} 条预设提示词`,
|
||||||
Edit: "当前对话设置",
|
Edit: "当前对话设置",
|
||||||
Add: "新增预设对话",
|
Add: "新增预设对话",
|
||||||
},
|
},
|
||||||
|
@ -163,12 +163,11 @@ const en: RequiredLocaleType = {
|
|||||||
Error: "Something went wrong, please try again later.",
|
Error: "Something went wrong, please try again later.",
|
||||||
Prompt: {
|
Prompt: {
|
||||||
History: (content: string) =>
|
History: (content: string) =>
|
||||||
"This is a summary of the chat history between the AI and the user as a recap: " +
|
"This is a summary of the chat history as a recap: " + content,
|
||||||
content,
|
|
||||||
Topic:
|
Topic:
|
||||||
"Please generate a four to five word title summarizing our conversation without any lead-in, punctuation, quotation marks, periods, symbols, or additional text. Remove enclosing quotation marks.",
|
"Please generate a four to five word title summarizing our conversation without any lead-in, punctuation, quotation marks, periods, symbols, or additional text. Remove enclosing quotation marks.",
|
||||||
Summarize:
|
Summarize:
|
||||||
"Summarize our discussion briefly in 200 words or less to use as a prompt for future context.",
|
"Summarize the discussion briefly in 200 words or less to use as a prompt for future context.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Copy: {
|
Copy: {
|
||||||
|
@ -45,6 +45,7 @@ export interface ChatSession {
|
|||||||
stat: ChatStat;
|
stat: ChatStat;
|
||||||
lastUpdate: number;
|
lastUpdate: number;
|
||||||
lastSummarizeIndex: number;
|
lastSummarizeIndex: number;
|
||||||
|
clearContextIndex?: number;
|
||||||
|
|
||||||
mask: Mask;
|
mask: Mask;
|
||||||
}
|
}
|
||||||
@ -341,7 +342,12 @@ export const useChatStore = create<ChatStore>()(
|
|||||||
getMessagesWithMemory() {
|
getMessagesWithMemory() {
|
||||||
const session = get().currentSession();
|
const session = get().currentSession();
|
||||||
const modelConfig = session.mask.modelConfig;
|
const modelConfig = session.mask.modelConfig;
|
||||||
const messages = session.messages.filter((msg) => !msg.isError);
|
|
||||||
|
// wont send cleared context messages
|
||||||
|
const clearedContextMessages = session.messages.slice(
|
||||||
|
(session.clearContextIndex ?? -1) + 1,
|
||||||
|
);
|
||||||
|
const messages = clearedContextMessages.filter((msg) => !msg.isError);
|
||||||
const n = messages.length;
|
const n = messages.length;
|
||||||
|
|
||||||
const context = session.mask.context.slice();
|
const context = session.mask.context.slice();
|
||||||
@ -362,17 +368,17 @@ export const useChatStore = create<ChatStore>()(
|
|||||||
n - modelConfig.historyMessageCount,
|
n - modelConfig.historyMessageCount,
|
||||||
);
|
);
|
||||||
const longTermMemoryMessageIndex = session.lastSummarizeIndex;
|
const longTermMemoryMessageIndex = session.lastSummarizeIndex;
|
||||||
const oldestIndex = Math.max(
|
const mostRecentIndex = Math.max(
|
||||||
shortTermMemoryMessageIndex,
|
shortTermMemoryMessageIndex,
|
||||||
longTermMemoryMessageIndex,
|
longTermMemoryMessageIndex,
|
||||||
);
|
);
|
||||||
const threshold = modelConfig.compressMessageLengthThreshold;
|
const threshold = modelConfig.compressMessageLengthThreshold * 2;
|
||||||
|
|
||||||
// get recent messages as many as possible
|
// get recent messages as many as possible
|
||||||
const reversedRecentMessages = [];
|
const reversedRecentMessages = [];
|
||||||
for (
|
for (
|
||||||
let i = n - 1, count = 0;
|
let i = n - 1, count = 0;
|
||||||
i >= oldestIndex && count < threshold;
|
i >= mostRecentIndex && count < threshold;
|
||||||
i -= 1
|
i -= 1
|
||||||
) {
|
) {
|
||||||
const msg = messages[i];
|
const msg = messages[i];
|
||||||
@ -410,15 +416,15 @@ export const useChatStore = create<ChatStore>()(
|
|||||||
const session = get().currentSession();
|
const session = get().currentSession();
|
||||||
|
|
||||||
// remove error messages if any
|
// remove error messages if any
|
||||||
const cleanMessages = session.messages.filter((msg) => !msg.isError);
|
const messages = session.messages;
|
||||||
|
|
||||||
// should summarize topic after chating more than 50 words
|
// should summarize topic after chating more than 50 words
|
||||||
const SUMMARIZE_MIN_LEN = 50;
|
const SUMMARIZE_MIN_LEN = 50;
|
||||||
if (
|
if (
|
||||||
session.topic === DEFAULT_TOPIC &&
|
session.topic === DEFAULT_TOPIC &&
|
||||||
countMessages(cleanMessages) >= SUMMARIZE_MIN_LEN
|
countMessages(messages) >= SUMMARIZE_MIN_LEN
|
||||||
) {
|
) {
|
||||||
const topicMessages = cleanMessages.concat(
|
const topicMessages = messages.concat(
|
||||||
createMessage({
|
createMessage({
|
||||||
role: "user",
|
role: "user",
|
||||||
content: Locale.Store.Prompt.Topic,
|
content: Locale.Store.Prompt.Topic,
|
||||||
@ -440,9 +446,13 @@ export const useChatStore = create<ChatStore>()(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const modelConfig = session.mask.modelConfig;
|
const modelConfig = session.mask.modelConfig;
|
||||||
let toBeSummarizedMsgs = cleanMessages.slice(
|
const summarizeIndex = Math.max(
|
||||||
session.lastSummarizeIndex,
|
session.lastSummarizeIndex,
|
||||||
|
session.clearContextIndex ?? 0,
|
||||||
);
|
);
|
||||||
|
let toBeSummarizedMsgs = messages
|
||||||
|
.filter((msg) => !msg.isError)
|
||||||
|
.slice(summarizeIndex);
|
||||||
|
|
||||||
const historyMsgLength = countMessages(toBeSummarizedMsgs);
|
const historyMsgLength = countMessages(toBeSummarizedMsgs);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user