record each message's token and hide status, calc postBeginIndex based on token
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
const CHATGPT_API_WEB_VERSION = "v1.2.2";
|
const CHATGPT_API_WEB_VERSION = "v1.3.0";
|
||||||
|
|
||||||
export default CHATGPT_API_WEB_VERSION;
|
export default CHATGPT_API_WEB_VERSION;
|
||||||
|
|||||||
20
src/app.tsx
20
src/app.tsx
@@ -1,17 +1,22 @@
|
|||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
import "./global.css";
|
import "./global.css";
|
||||||
|
|
||||||
import { Message } from "./chatgpt";
|
import { calculate_token_length, Message } from "./chatgpt";
|
||||||
import getDefaultParams from "./getDefaultParam";
|
import getDefaultParams from "./getDefaultParam";
|
||||||
import ChatBOX from "./chatbox";
|
import ChatBOX from "./chatbox";
|
||||||
import { options } from "./settings";
|
import { options } from "./settings";
|
||||||
|
|
||||||
import CHATGPT_API_WEB_VERSION from './CHATGPT_API_WEB_VERSION'
|
import CHATGPT_API_WEB_VERSION from "./CHATGPT_API_WEB_VERSION";
|
||||||
|
|
||||||
|
export interface ChatStoreMessage extends Message {
|
||||||
|
hide: boolean;
|
||||||
|
token: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ChatStore {
|
export interface ChatStore {
|
||||||
chatgpt_api_web_version: string;
|
chatgpt_api_web_version: string;
|
||||||
systemMessageContent: string;
|
systemMessageContent: string;
|
||||||
history: Message[];
|
history: ChatStoreMessage[];
|
||||||
postBeginIndex: number;
|
postBeginIndex: number;
|
||||||
tokenMargin: number;
|
tokenMargin: number;
|
||||||
totalTokens: number;
|
totalTokens: number;
|
||||||
@@ -86,7 +91,14 @@ export function App() {
|
|||||||
if (ret.model === undefined) ret.model = "gpt-3.5-turbo";
|
if (ret.model === undefined) ret.model = "gpt-3.5-turbo";
|
||||||
if (ret.responseModelName === undefined) ret.responseModelName = "";
|
if (ret.responseModelName === undefined) ret.responseModelName = "";
|
||||||
if (ret.chatgpt_api_web_version === undefined)
|
if (ret.chatgpt_api_web_version === undefined)
|
||||||
ret.chatgpt_api_web_version = CHATGPT_API_WEB_VERSION;
|
// this is from old version becasue it is undefined,
|
||||||
|
// so no higher than v1.3.0
|
||||||
|
ret.chatgpt_api_web_version = "v1.2.2";
|
||||||
|
for (const message of ret.history) {
|
||||||
|
if (message.hide === undefined) message.hide = false;
|
||||||
|
if (message.token === undefined)
|
||||||
|
message.token = calculate_token_length(message.content);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { createRef } from "preact";
|
import { createRef } from "preact";
|
||||||
import { StateUpdater, useEffect, useState } from "preact/hooks";
|
import { StateUpdater, useEffect, useState } from "preact/hooks";
|
||||||
import type { ChatStore } from "./app";
|
import type { ChatStore } from "./app";
|
||||||
import ChatGPT, { ChunkMessage, FetchResponse } from "./chatgpt";
|
import ChatGPT, {
|
||||||
|
calculate_token_length,
|
||||||
|
ChunkMessage,
|
||||||
|
FetchResponse,
|
||||||
|
} from "./chatgpt";
|
||||||
import Message from "./message";
|
import Message from "./message";
|
||||||
import Settings from "./settings";
|
import Settings from "./settings";
|
||||||
|
|
||||||
@@ -77,17 +81,16 @@ export default function ChatBOX(props: {
|
|||||||
setShowGenerating(false);
|
setShowGenerating(false);
|
||||||
|
|
||||||
// console.log("push to history", allChunkMessage);
|
// console.log("push to history", allChunkMessage);
|
||||||
|
const content = allChunkMessage.join("");
|
||||||
chatStore.history.push({
|
chatStore.history.push({
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
content: allChunkMessage.join(""),
|
content,
|
||||||
|
hide: false,
|
||||||
|
token: calculate_token_length(content),
|
||||||
});
|
});
|
||||||
// manually copy status from client to chatStore
|
// manually copy status from client to chatStore
|
||||||
chatStore.maxTokens = client.max_tokens;
|
chatStore.maxTokens = client.max_tokens;
|
||||||
chatStore.tokenMargin = client.tokens_margin;
|
chatStore.tokenMargin = client.tokens_margin;
|
||||||
chatStore.totalTokens =
|
|
||||||
client.total_tokens +
|
|
||||||
39 +
|
|
||||||
client.calculate_token_length(allChunkMessage.join(""));
|
|
||||||
setChatStore({ ...chatStore });
|
setChatStore({ ...chatStore });
|
||||||
setGeneratingMessage("");
|
setGeneratingMessage("");
|
||||||
setShowGenerating(false);
|
setShowGenerating(false);
|
||||||
@@ -100,7 +103,12 @@ export default function ChatBOX(props: {
|
|||||||
const data = (await response.json()) as FetchResponse;
|
const data = (await response.json()) as FetchResponse;
|
||||||
chatStore.responseModelName = data.model ?? "";
|
chatStore.responseModelName = data.model ?? "";
|
||||||
const content = client.processFetchResponse(data);
|
const content = client.processFetchResponse(data);
|
||||||
chatStore.history.push({ role: "assistant", content });
|
chatStore.history.push({
|
||||||
|
role: "assistant",
|
||||||
|
content,
|
||||||
|
hide: false,
|
||||||
|
token: data.usage.completion_tokens ?? calculate_token_length(content),
|
||||||
|
});
|
||||||
setShowGenerating(false);
|
setShowGenerating(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -109,11 +117,35 @@ export default function ChatBOX(props: {
|
|||||||
// manually copy status from chatStore to client
|
// manually copy status from chatStore to client
|
||||||
client.apiEndpoint = chatStore.apiEndpoint;
|
client.apiEndpoint = chatStore.apiEndpoint;
|
||||||
client.sysMessageContent = chatStore.systemMessageContent;
|
client.sysMessageContent = chatStore.systemMessageContent;
|
||||||
client.messages = chatStore.history.slice(chatStore.postBeginIndex);
|
client.tokens_margin = chatStore.tokenMargin;
|
||||||
|
client.messages = chatStore.history
|
||||||
|
.slice(chatStore.postBeginIndex)
|
||||||
|
// only copy non hidden message
|
||||||
|
.filter(({ hide }) => !hide)
|
||||||
|
// only copy content and role attribute to client for posting
|
||||||
|
.map(({ content, role }) => {
|
||||||
|
return {
|
||||||
|
content,
|
||||||
|
role,
|
||||||
|
};
|
||||||
|
});
|
||||||
client.model = chatStore.model;
|
client.model = chatStore.model;
|
||||||
client.max_tokens = chatStore.maxTokens;
|
client.max_tokens = chatStore.maxTokens;
|
||||||
// try forget message before sending request
|
|
||||||
client.forgetSomeMessages();
|
// todo move code
|
||||||
|
const max = chatStore.maxTokens - chatStore.tokenMargin;
|
||||||
|
let sum = 0;
|
||||||
|
chatStore.postBeginIndex = chatStore.history.filter(
|
||||||
|
({ hide }) => !hide
|
||||||
|
).length;
|
||||||
|
for (const msg of chatStore.history.slice().reverse()) {
|
||||||
|
sum += msg.token;
|
||||||
|
if (sum > max) break;
|
||||||
|
chatStore.postBeginIndex -= 1;
|
||||||
|
}
|
||||||
|
chatStore.postBeginIndex =
|
||||||
|
chatStore.postBeginIndex < 0 ? 0 : chatStore.postBeginIndex;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setShowGenerating(true);
|
setShowGenerating(true);
|
||||||
const response = await client._fetch(chatStore.streamMode);
|
const response = await client._fetch(chatStore.streamMode);
|
||||||
@@ -129,11 +161,21 @@ export default function ChatBOX(props: {
|
|||||||
chatStore.maxTokens = client.max_tokens;
|
chatStore.maxTokens = client.max_tokens;
|
||||||
chatStore.tokenMargin = client.tokens_margin;
|
chatStore.tokenMargin = client.tokens_margin;
|
||||||
chatStore.totalTokens = client.total_tokens;
|
chatStore.totalTokens = client.total_tokens;
|
||||||
// when total token > max token - margin token:
|
|
||||||
// ChatGPT will "forgot" some historical message
|
// todo move code
|
||||||
// so client.message.length will be less than chatStore.history.length
|
const max = chatStore.maxTokens - chatStore.tokenMargin;
|
||||||
|
let sum = 0;
|
||||||
|
chatStore.postBeginIndex = chatStore.history.filter(
|
||||||
|
({ hide }) => !hide
|
||||||
|
).length;
|
||||||
|
for (const msg of chatStore.history.slice().reverse()) {
|
||||||
|
sum += msg.token;
|
||||||
|
if (sum > max) break;
|
||||||
|
chatStore.postBeginIndex -= 1;
|
||||||
|
}
|
||||||
chatStore.postBeginIndex =
|
chatStore.postBeginIndex =
|
||||||
chatStore.history.length - client.messages.length;
|
chatStore.postBeginIndex < 0 ? 0 : chatStore.postBeginIndex;
|
||||||
|
|
||||||
console.log("postBeginIndex", chatStore.postBeginIndex);
|
console.log("postBeginIndex", chatStore.postBeginIndex);
|
||||||
setChatStore({ ...chatStore });
|
setChatStore({ ...chatStore });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -153,7 +195,12 @@ export default function ChatBOX(props: {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
chatStore.responseModelName = "";
|
chatStore.responseModelName = "";
|
||||||
chatStore.history.push({ role: "user", content: inputMsg.trim() });
|
chatStore.history.push({
|
||||||
|
role: "user",
|
||||||
|
content: inputMsg.trim(),
|
||||||
|
hide: false,
|
||||||
|
token: calculate_token_length(inputMsg.trim()),
|
||||||
|
});
|
||||||
// manually calculate token length
|
// manually calculate token length
|
||||||
chatStore.totalTokens += client.calculate_token_length(inputMsg.trim());
|
chatStore.totalTokens += client.calculate_token_length(inputMsg.trim());
|
||||||
client.total_tokens += client.calculate_token_length(inputMsg.trim());
|
client.total_tokens += client.calculate_token_length(inputMsg.trim());
|
||||||
@@ -191,7 +238,9 @@ export default function ChatBOX(props: {
|
|||||||
Tokens: {chatStore.totalTokens} / {chatStore.maxTokens}
|
Tokens: {chatStore.totalTokens} / {chatStore.maxTokens}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
<span>{chatStore.model}</span>{" "}
|
<span>{chatStore.model}</span>{" "}
|
||||||
<span>Messages: {chatStore.history.length}</span>{" "}
|
<span>
|
||||||
|
Messages: {chatStore.history.filter(({ hide }) => !hide).length}
|
||||||
|
</span>{" "}
|
||||||
<span>Cut: {chatStore.postBeginIndex}</span>
|
<span>Cut: {chatStore.postBeginIndex}</span>
|
||||||
</div>
|
</div>
|
||||||
</p>
|
</p>
|
||||||
@@ -252,6 +301,23 @@ export default function ChatBOX(props: {
|
|||||||
{chatStore.responseModelName && (
|
{chatStore.responseModelName && (
|
||||||
<p className="p-2 my-2 text-center opacity-50 dark:text-white">
|
<p className="p-2 my-2 text-center opacity-50 dark:text-white">
|
||||||
Generated by {chatStore.responseModelName}
|
Generated by {chatStore.responseModelName}
|
||||||
|
{chatStore.postBeginIndex !== 0 && (
|
||||||
|
<>
|
||||||
|
<br />
|
||||||
|
提示:会话过长,已裁切前 {chatStore.postBeginIndex} 条消息
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{chatStore.chatgpt_api_web_version < "v1.3.0" && (
|
||||||
|
<>
|
||||||
|
<br />
|
||||||
|
提示:当前会话版本 {chatStore.chatgpt_api_web_version}。
|
||||||
|
<br />
|
||||||
|
v1.3.0
|
||||||
|
引入与旧版不兼容的消息裁切算法。继续使用旧版可能会导致消息裁切过多或过少(表现为失去上下文或输出不完整)。
|
||||||
|
<br />
|
||||||
|
请在左上角创建新会话:)
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{showRetry && (
|
{showRetry && (
|
||||||
|
|||||||
@@ -15,43 +15,71 @@ export default function Message(props: Props) {
|
|||||||
chat.role === "user" ? "left-0" : "right-0"
|
chat.role === "user" ? "left-0" : "right-0"
|
||||||
}`}
|
}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (
|
chatStore.history[messageIndex].hide =
|
||||||
confirm(
|
!chatStore.history[messageIndex].hide;
|
||||||
`Are you sure to delete this message?\n${chat.content.slice(
|
|
||||||
0,
|
// todo move code
|
||||||
39
|
const max = chatStore.maxTokens - chatStore.tokenMargin;
|
||||||
)}...`
|
let sum = 0;
|
||||||
)
|
chatStore.postBeginIndex = chatStore.history.filter(
|
||||||
) {
|
({ hide }) => !hide
|
||||||
chatStore.history.splice(messageIndex, 1);
|
).length;
|
||||||
chatStore.postBeginIndex = Math.max(chatStore.postBeginIndex - 1, 0);
|
for (const msg of chatStore.history.slice().reverse()) {
|
||||||
chatStore.totalTokens = Math.max(
|
sum += msg.token;
|
||||||
0,
|
if (sum > max) break;
|
||||||
chatStore.totalTokens - calculate_token_length(chat.content)
|
chatStore.postBeginIndex -= 1;
|
||||||
);
|
|
||||||
setChatStore({ ...chatStore });
|
|
||||||
}
|
}
|
||||||
|
chatStore.postBeginIndex =
|
||||||
|
chatStore.postBeginIndex < 0 ? 0 : chatStore.postBeginIndex;
|
||||||
|
|
||||||
|
//chatStore.totalTokens =
|
||||||
|
chatStore.totalTokens = 0;
|
||||||
|
for (const i of chatStore.history
|
||||||
|
.filter(({ hide }) => !hide)
|
||||||
|
.slice(chatStore.postBeginIndex)
|
||||||
|
.map(({ token }) => token)) {
|
||||||
|
chatStore.totalTokens += i;
|
||||||
|
}
|
||||||
|
setChatStore({ ...chatStore });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
🗑️
|
🗑️
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
className={`flex ${
|
{chatStore.postBeginIndex !== 0 &&
|
||||||
chat.role === "assistant" ? "justify-start" : "justify-end"
|
!chatStore.history[messageIndex].hide &&
|
||||||
}`}
|
chatStore.postBeginIndex ===
|
||||||
>
|
chatStore.history.slice(0, messageIndex).filter(({ hide }) => !hide)
|
||||||
|
.length && (
|
||||||
|
<div className="flex items-center relative justify-center">
|
||||||
|
<hr className="w-full h-px my-4 border-0 bg-slate-800 dark:bg-white" />
|
||||||
|
<span className="absolute px-3 bg-slate-800 text-white rounded p-1 dark:bg-white dark:text-black">
|
||||||
|
Above messages are "forgotten"
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div
|
<div
|
||||||
className={`relative w-fit p-2 rounded my-2 ${
|
className={`flex ${
|
||||||
chat.role === "assistant"
|
chat.role === "assistant" ? "justify-start" : "justify-end"
|
||||||
? "bg-white dark:bg-gray-700 dark:text-white"
|
|
||||||
: "bg-green-400"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<p className="message-content">{chat.content}</p>
|
<div
|
||||||
<DeleteIcon />
|
className={`relative w-fit p-2 rounded my-2 ${
|
||||||
|
chat.role === "assistant"
|
||||||
|
? "bg-white dark:bg-gray-700 dark:text-white"
|
||||||
|
: "bg-green-400"
|
||||||
|
} ${chat.hide ? "opacity-50" : ""}`}
|
||||||
|
>
|
||||||
|
<p className="message-content">
|
||||||
|
{chat.hide
|
||||||
|
? chat.content.split("\n")[0].slice(0, 16) + "... (deleted)"
|
||||||
|
: chat.content}
|
||||||
|
</p>
|
||||||
|
<DeleteIcon />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user