|
|
@ -5,7 +5,7 @@ import { type ChatCompletionResponseMessage } from "openai";
|
|
|
|
import { requestChatStream, requestWithPrompt } from "./requests";
|
|
|
|
import { requestChatStream, requestWithPrompt } from "./requests";
|
|
|
|
import { trimTopic } from "./utils";
|
|
|
|
import { trimTopic } from "./utils";
|
|
|
|
|
|
|
|
|
|
|
|
import Locale from './locales'
|
|
|
|
import Locale from "./locales";
|
|
|
|
|
|
|
|
|
|
|
|
export type Message = ChatCompletionResponseMessage & {
|
|
|
|
export type Message = ChatCompletionResponseMessage & {
|
|
|
|
date: string;
|
|
|
|
date: string;
|
|
|
@ -26,7 +26,7 @@ export enum Theme {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface ChatConfig {
|
|
|
|
export interface ChatConfig {
|
|
|
|
maxToken?: number
|
|
|
|
maxToken?: number;
|
|
|
|
historyMessageCount: number; // -1 means all
|
|
|
|
historyMessageCount: number; // -1 means all
|
|
|
|
compressMessageLengthThreshold: number;
|
|
|
|
compressMessageLengthThreshold: number;
|
|
|
|
sendBotMessages: boolean; // send bot's message or not
|
|
|
|
sendBotMessages: boolean; // send bot's message or not
|
|
|
@ -34,6 +34,78 @@ export interface ChatConfig {
|
|
|
|
avatar: string;
|
|
|
|
avatar: string;
|
|
|
|
theme: Theme;
|
|
|
|
theme: Theme;
|
|
|
|
tightBorder: boolean;
|
|
|
|
tightBorder: boolean;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
modelConfig: {
|
|
|
|
|
|
|
|
model: string;
|
|
|
|
|
|
|
|
temperature: number;
|
|
|
|
|
|
|
|
max_tokens: number;
|
|
|
|
|
|
|
|
presence_penalty: number;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export type ModelConfig = ChatConfig["modelConfig"];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const ALL_MODELS = [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "gpt-4",
|
|
|
|
|
|
|
|
available: false,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "gpt-4-0314",
|
|
|
|
|
|
|
|
available: false,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "gpt-4-32k",
|
|
|
|
|
|
|
|
available: false,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "gpt-4-32k-0314",
|
|
|
|
|
|
|
|
available: false,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "gpt-3.5-turbo",
|
|
|
|
|
|
|
|
available: true,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "gpt-3.5-turbo-0301",
|
|
|
|
|
|
|
|
available: true,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function isValidModel(name: string) {
|
|
|
|
|
|
|
|
return ALL_MODELS.some((m) => m.name === name && m.available);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function isValidNumber(x: number, min: number, max: number) {
|
|
|
|
|
|
|
|
return typeof x === "number" && x <= max && x >= min;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function filterConfig(config: ModelConfig): Partial<ModelConfig> {
|
|
|
|
|
|
|
|
const validator: {
|
|
|
|
|
|
|
|
[k in keyof ModelConfig]: (x: ModelConfig[keyof ModelConfig]) => boolean;
|
|
|
|
|
|
|
|
} = {
|
|
|
|
|
|
|
|
model(x) {
|
|
|
|
|
|
|
|
return isValidModel(x as string);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
max_tokens(x) {
|
|
|
|
|
|
|
|
return isValidNumber(x as number, 100, 4000);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
presence_penalty(x) {
|
|
|
|
|
|
|
|
return isValidNumber(x as number, -2, 2);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
temperature(x) {
|
|
|
|
|
|
|
|
return isValidNumber(x as number, 0, 1);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Object.keys(validator).forEach((k) => {
|
|
|
|
|
|
|
|
const key = k as keyof ModelConfig;
|
|
|
|
|
|
|
|
if (!validator[key](config[key])) {
|
|
|
|
|
|
|
|
delete config[key];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const DEFAULT_CONFIG: ChatConfig = {
|
|
|
|
const DEFAULT_CONFIG: ChatConfig = {
|
|
|
@ -44,6 +116,13 @@ const DEFAULT_CONFIG: ChatConfig = {
|
|
|
|
avatar: "1f603",
|
|
|
|
avatar: "1f603",
|
|
|
|
theme: Theme.Auto as Theme,
|
|
|
|
theme: Theme.Auto as Theme,
|
|
|
|
tightBorder: false,
|
|
|
|
tightBorder: false,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
modelConfig: {
|
|
|
|
|
|
|
|
model: "gpt-3.5-turbo",
|
|
|
|
|
|
|
|
temperature: 1,
|
|
|
|
|
|
|
|
max_tokens: 2000,
|
|
|
|
|
|
|
|
presence_penalty: 0,
|
|
|
|
|
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export interface ChatStat {
|
|
|
|
export interface ChatStat {
|
|
|
@ -107,7 +186,7 @@ interface ChatStore {
|
|
|
|
updater: (message?: Message) => void
|
|
|
|
updater: (message?: Message) => void
|
|
|
|
) => void;
|
|
|
|
) => void;
|
|
|
|
getMessagesWithMemory: () => Message[];
|
|
|
|
getMessagesWithMemory: () => Message[];
|
|
|
|
getMemoryPrompt: () => Message,
|
|
|
|
getMemoryPrompt: () => Message;
|
|
|
|
|
|
|
|
|
|
|
|
getConfig: () => ChatConfig;
|
|
|
|
getConfig: () => ChatConfig;
|
|
|
|
resetConfig: () => void;
|
|
|
|
resetConfig: () => void;
|
|
|
@ -193,9 +272,9 @@ export const useChatStore = create<ChatStore>()(
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
onNewMessage(message) {
|
|
|
|
onNewMessage(message) {
|
|
|
|
get().updateCurrentSession(session => {
|
|
|
|
get().updateCurrentSession((session) => {
|
|
|
|
session.lastUpdate = new Date().toLocaleString()
|
|
|
|
session.lastUpdate = new Date().toLocaleString();
|
|
|
|
})
|
|
|
|
});
|
|
|
|
get().updateStat(message);
|
|
|
|
get().updateStat(message);
|
|
|
|
get().summarizeSession();
|
|
|
|
get().summarizeSession();
|
|
|
|
},
|
|
|
|
},
|
|
|
@ -215,8 +294,8 @@ export const useChatStore = create<ChatStore>()(
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// get recent messages
|
|
|
|
// get recent messages
|
|
|
|
const recentMessages = get().getMessagesWithMemory()
|
|
|
|
const recentMessages = get().getMessagesWithMemory();
|
|
|
|
const sendMessages = recentMessages.concat(userMessage)
|
|
|
|
const sendMessages = recentMessages.concat(userMessage);
|
|
|
|
|
|
|
|
|
|
|
|
// save user's and bot's message
|
|
|
|
// save user's and bot's message
|
|
|
|
get().updateCurrentSession((session) => {
|
|
|
|
get().updateCurrentSession((session) => {
|
|
|
@ -224,12 +303,12 @@ export const useChatStore = create<ChatStore>()(
|
|
|
|
session.messages.push(botMessage);
|
|
|
|
session.messages.push(botMessage);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
console.log('[User Input] ', sendMessages)
|
|
|
|
console.log("[User Input] ", sendMessages);
|
|
|
|
requestChatStream(sendMessages, {
|
|
|
|
requestChatStream(sendMessages, {
|
|
|
|
onMessage(content, done) {
|
|
|
|
onMessage(content, done) {
|
|
|
|
if (done) {
|
|
|
|
if (done) {
|
|
|
|
botMessage.streaming = false;
|
|
|
|
botMessage.streaming = false;
|
|
|
|
get().onNewMessage(botMessage)
|
|
|
|
get().onNewMessage(botMessage);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
botMessage.content = content;
|
|
|
|
botMessage.content = content;
|
|
|
|
set(() => ({}));
|
|
|
|
set(() => ({}));
|
|
|
@ -241,32 +320,35 @@ export const useChatStore = create<ChatStore>()(
|
|
|
|
set(() => ({}));
|
|
|
|
set(() => ({}));
|
|
|
|
},
|
|
|
|
},
|
|
|
|
filterBot: !get().config.sendBotMessages,
|
|
|
|
filterBot: !get().config.sendBotMessages,
|
|
|
|
|
|
|
|
modelConfig: get().config.modelConfig,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getMemoryPrompt() {
|
|
|
|
getMemoryPrompt() {
|
|
|
|
const session = get().currentSession()
|
|
|
|
const session = get().currentSession();
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
role: 'system',
|
|
|
|
role: "system",
|
|
|
|
content: Locale.Store.Prompt.History(session.memoryPrompt),
|
|
|
|
content: Locale.Store.Prompt.History(session.memoryPrompt),
|
|
|
|
date: ''
|
|
|
|
date: "",
|
|
|
|
} as Message
|
|
|
|
} as Message;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getMessagesWithMemory() {
|
|
|
|
getMessagesWithMemory() {
|
|
|
|
const session = get().currentSession()
|
|
|
|
const session = get().currentSession();
|
|
|
|
const config = get().config
|
|
|
|
const config = get().config;
|
|
|
|
const n = session.messages.length
|
|
|
|
const n = session.messages.length;
|
|
|
|
const recentMessages = session.messages.slice(n - config.historyMessageCount);
|
|
|
|
const recentMessages = session.messages.slice(
|
|
|
|
|
|
|
|
n - config.historyMessageCount
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const memoryPrompt = get().getMemoryPrompt()
|
|
|
|
const memoryPrompt = get().getMemoryPrompt();
|
|
|
|
|
|
|
|
|
|
|
|
if (session.memoryPrompt) {
|
|
|
|
if (session.memoryPrompt) {
|
|
|
|
recentMessages.unshift(memoryPrompt)
|
|
|
|
recentMessages.unshift(memoryPrompt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return recentMessages
|
|
|
|
return recentMessages;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
updateMessage(
|
|
|
|
updateMessage(
|
|
|
@ -286,49 +368,63 @@ export const useChatStore = create<ChatStore>()(
|
|
|
|
|
|
|
|
|
|
|
|
if (session.topic === DEFAULT_TOPIC && session.messages.length >= 3) {
|
|
|
|
if (session.topic === DEFAULT_TOPIC && session.messages.length >= 3) {
|
|
|
|
// should summarize topic
|
|
|
|
// should summarize topic
|
|
|
|
requestWithPrompt(
|
|
|
|
requestWithPrompt(session.messages, Locale.Store.Prompt.Topic).then(
|
|
|
|
session.messages,
|
|
|
|
(res) => {
|
|
|
|
Locale.Store.Prompt.Topic
|
|
|
|
|
|
|
|
).then((res) => {
|
|
|
|
|
|
|
|
get().updateCurrentSession(
|
|
|
|
get().updateCurrentSession(
|
|
|
|
(session) => (session.topic = trimTopic(res))
|
|
|
|
(session) => (session.topic = trimTopic(res))
|
|
|
|
);
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const config = get().config
|
|
|
|
const config = get().config;
|
|
|
|
let toBeSummarizedMsgs = session.messages.slice(session.lastSummarizeIndex)
|
|
|
|
let toBeSummarizedMsgs = session.messages.slice(
|
|
|
|
const historyMsgLength = toBeSummarizedMsgs.reduce((pre, cur) => pre + cur.content.length, 0)
|
|
|
|
session.lastSummarizeIndex
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
const historyMsgLength = toBeSummarizedMsgs.reduce(
|
|
|
|
|
|
|
|
(pre, cur) => pre + cur.content.length,
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (historyMsgLength > 4000) {
|
|
|
|
if (historyMsgLength > 4000) {
|
|
|
|
toBeSummarizedMsgs = toBeSummarizedMsgs.slice(-config.historyMessageCount)
|
|
|
|
toBeSummarizedMsgs = toBeSummarizedMsgs.slice(
|
|
|
|
|
|
|
|
-config.historyMessageCount
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// add memory prompt
|
|
|
|
// add memory prompt
|
|
|
|
toBeSummarizedMsgs.unshift(get().getMemoryPrompt())
|
|
|
|
toBeSummarizedMsgs.unshift(get().getMemoryPrompt());
|
|
|
|
|
|
|
|
|
|
|
|
const lastSummarizeIndex = session.messages.length
|
|
|
|
const lastSummarizeIndex = session.messages.length;
|
|
|
|
|
|
|
|
|
|
|
|
console.log('[Chat History] ', toBeSummarizedMsgs, historyMsgLength, config.compressMessageLengthThreshold)
|
|
|
|
console.log(
|
|
|
|
|
|
|
|
"[Chat History] ",
|
|
|
|
|
|
|
|
toBeSummarizedMsgs,
|
|
|
|
|
|
|
|
historyMsgLength,
|
|
|
|
|
|
|
|
config.compressMessageLengthThreshold
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (historyMsgLength > config.compressMessageLengthThreshold) {
|
|
|
|
if (historyMsgLength > config.compressMessageLengthThreshold) {
|
|
|
|
requestChatStream(toBeSummarizedMsgs.concat({
|
|
|
|
requestChatStream(
|
|
|
|
role: 'system',
|
|
|
|
toBeSummarizedMsgs.concat({
|
|
|
|
|
|
|
|
role: "system",
|
|
|
|
content: Locale.Store.Prompt.Summarize,
|
|
|
|
content: Locale.Store.Prompt.Summarize,
|
|
|
|
date: ''
|
|
|
|
date: "",
|
|
|
|
}), {
|
|
|
|
}),
|
|
|
|
|
|
|
|
{
|
|
|
|
filterBot: false,
|
|
|
|
filterBot: false,
|
|
|
|
onMessage(message, done) {
|
|
|
|
onMessage(message, done) {
|
|
|
|
session.memoryPrompt = message
|
|
|
|
session.memoryPrompt = message;
|
|
|
|
if (done) {
|
|
|
|
if (done) {
|
|
|
|
console.log('[Memory] ', session.memoryPrompt)
|
|
|
|
console.log("[Memory] ", session.memoryPrompt);
|
|
|
|
session.lastSummarizeIndex = lastSummarizeIndex
|
|
|
|
session.lastSummarizeIndex = lastSummarizeIndex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
onError(error) {
|
|
|
|
onError(error) {
|
|
|
|
console.error('[Summarize] ', error)
|
|
|
|
console.error("[Summarize] ", error);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
@ -348,8 +444,8 @@ export const useChatStore = create<ChatStore>()(
|
|
|
|
|
|
|
|
|
|
|
|
clearAllData() {
|
|
|
|
clearAllData() {
|
|
|
|
if (confirm(Locale.Store.ConfirmClearAll)) {
|
|
|
|
if (confirm(Locale.Store.ConfirmClearAll)) {
|
|
|
|
localStorage.clear()
|
|
|
|
localStorage.clear();
|
|
|
|
location.reload()
|
|
|
|
location.reload();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|