mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2026-04-28 05:54:24 +08:00
Compare commits
4 Commits
f80da8a263
...
update-max
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebe617b733 | ||
|
|
c139038e01 | ||
|
|
4a7fd3a380 | ||
|
|
c98dc31cdf |
18
.github/workflows/docker.yml
vendored
18
.github/workflows/docker.yml
vendored
@@ -19,26 +19,26 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
-
|
-
|
||||||
name: Extract metadata (tags, labels) for Docker
|
name: Extract metadata (tags, labels) for Docker
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: ${{ secrets.DOCKER_USERNAME }}/chatgpt-next-web
|
images: yidadaa/chatgpt-next-web
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value=latest
|
type=raw,value=latest
|
||||||
type=ref,event=tag
|
type=ref,event=tag
|
||||||
|
|
||||||
-
|
-
|
||||||
name: Set up QEMU
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
-
|
-
|
||||||
name: Set up Docker Buildx
|
name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
-
|
-
|
||||||
name: Build and push Docker image
|
name: Build and push Docker image
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
@@ -49,4 +49,4 @@ jobs:
|
|||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export interface RequestPayload {
|
|||||||
presence_penalty: number;
|
presence_penalty: number;
|
||||||
frequency_penalty: number;
|
frequency_penalty: number;
|
||||||
top_p: number;
|
top_p: number;
|
||||||
max_tokens?: number;
|
max_completions_tokens?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DalleRequestPayload {
|
export interface DalleRequestPayload {
|
||||||
@@ -228,13 +228,16 @@ export class ChatGPTApi implements LLMApi {
|
|||||||
presence_penalty: !isO1 ? modelConfig.presence_penalty : 0,
|
presence_penalty: !isO1 ? modelConfig.presence_penalty : 0,
|
||||||
frequency_penalty: !isO1 ? modelConfig.frequency_penalty : 0,
|
frequency_penalty: !isO1 ? modelConfig.frequency_penalty : 0,
|
||||||
top_p: !isO1 ? modelConfig.top_p : 1,
|
top_p: !isO1 ? modelConfig.top_p : 1,
|
||||||
// max_tokens: Math.max(modelConfig.max_tokens, 1024),
|
// max_completions_tokens: Math.max(modelConfig.max_completions_tokens, 1024),
|
||||||
// Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore.
|
// Please do not ask me why not send max_completions_tokens, no reason, this param is just shit, I dont want to explain anymore.
|
||||||
};
|
};
|
||||||
|
|
||||||
// add max_tokens to vision model
|
// add max_completions_tokens to vision model
|
||||||
if (visionModel) {
|
if (visionModel) {
|
||||||
requestPayload["max_tokens"] = Math.max(modelConfig.max_tokens, 4000);
|
requestPayload["max_completions_tokens"] = Math.max(
|
||||||
|
modelConfig.max_completions_tokens,
|
||||||
|
4000,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ export function useCommand(commands: Commands = {}) {
|
|||||||
interface ChatCommands {
|
interface ChatCommands {
|
||||||
new?: Command;
|
new?: Command;
|
||||||
newm?: Command;
|
newm?: Command;
|
||||||
copy?: Command;
|
|
||||||
next?: Command;
|
next?: Command;
|
||||||
prev?: Command;
|
prev?: Command;
|
||||||
clear?: Command;
|
clear?: Command;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import Logo from "../icons/logo.svg";
|
|||||||
import { useMobileScreen } from "@/app/utils";
|
import { useMobileScreen } from "@/app/utils";
|
||||||
import BotIcon from "../icons/bot.svg";
|
import BotIcon from "../icons/bot.svg";
|
||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
|
import { PasswordInput } from "./ui-lib";
|
||||||
import LeftIcon from "@/app/icons/left.svg";
|
import LeftIcon from "@/app/icons/left.svg";
|
||||||
import { safeLocalStorage } from "@/app/utils";
|
import { safeLocalStorage } from "@/app/utils";
|
||||||
import {
|
import {
|
||||||
@@ -60,36 +61,43 @@ export function AuthPage() {
|
|||||||
<div className={styles["auth-title"]}>{Locale.Auth.Title}</div>
|
<div className={styles["auth-title"]}>{Locale.Auth.Title}</div>
|
||||||
<div className={styles["auth-tips"]}>{Locale.Auth.Tips}</div>
|
<div className={styles["auth-tips"]}>{Locale.Auth.Tips}</div>
|
||||||
|
|
||||||
<input
|
<PasswordInput
|
||||||
className={styles["auth-input"]}
|
style={{ marginTop: "3vh", marginBottom: "3vh" }}
|
||||||
type="password"
|
aria={Locale.Settings.ShowPassword}
|
||||||
placeholder={Locale.Auth.Input}
|
aria-label={Locale.Auth.Input}
|
||||||
value={accessStore.accessCode}
|
value={accessStore.accessCode}
|
||||||
|
type="text"
|
||||||
|
placeholder={Locale.Auth.Input}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
accessStore.update(
|
accessStore.update(
|
||||||
(access) => (access.accessCode = e.currentTarget.value),
|
(access) => (access.accessCode = e.currentTarget.value),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!accessStore.hideUserApiKey ? (
|
{!accessStore.hideUserApiKey ? (
|
||||||
<>
|
<>
|
||||||
<div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div>
|
<div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div>
|
||||||
<input
|
<PasswordInput
|
||||||
className={styles["auth-input"]}
|
style={{ marginTop: "3vh", marginBottom: "3vh" }}
|
||||||
type="password"
|
aria={Locale.Settings.ShowPassword}
|
||||||
placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
|
aria-label={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
|
||||||
value={accessStore.openaiApiKey}
|
value={accessStore.openaiApiKey}
|
||||||
|
type="text"
|
||||||
|
placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
accessStore.update(
|
accessStore.update(
|
||||||
(access) => (access.openaiApiKey = e.currentTarget.value),
|
(access) => (access.openaiApiKey = e.currentTarget.value),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<input
|
<PasswordInput
|
||||||
className={styles["auth-input-second"]}
|
style={{ marginTop: "3vh", marginBottom: "3vh" }}
|
||||||
type="password"
|
aria={Locale.Settings.ShowPassword}
|
||||||
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
|
aria-label={Locale.Settings.Access.Google.ApiKey.Placeholder}
|
||||||
value={accessStore.googleApiKey}
|
value={accessStore.googleApiKey}
|
||||||
|
type="text"
|
||||||
|
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
accessStore.update(
|
accessStore.update(
|
||||||
(access) => (access.googleApiKey = e.currentTarget.value),
|
(access) => (access.googleApiKey = e.currentTarget.value),
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ import {
|
|||||||
getMessageImages,
|
getMessageImages,
|
||||||
isVisionModel,
|
isVisionModel,
|
||||||
isDalle3,
|
isDalle3,
|
||||||
removeOutdatedEntries,
|
|
||||||
showPlugins,
|
showPlugins,
|
||||||
safeLocalStorage,
|
safeLocalStorage,
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
@@ -985,7 +984,6 @@ function _Chat() {
|
|||||||
const chatCommands = useChatCommand({
|
const chatCommands = useChatCommand({
|
||||||
new: () => chatStore.newSession(),
|
new: () => chatStore.newSession(),
|
||||||
newm: () => navigate(Path.NewChat),
|
newm: () => navigate(Path.NewChat),
|
||||||
copy: () => chatStore.copySession(),
|
|
||||||
prev: () => chatStore.nextSession(-1),
|
prev: () => chatStore.nextSession(-1),
|
||||||
next: () => chatStore.nextSession(1),
|
next: () => chatStore.nextSession(1),
|
||||||
clear: () =>
|
clear: () =>
|
||||||
@@ -1117,20 +1115,10 @@ function _Chat() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deleteMessage = (msgId?: string) => {
|
const deleteMessage = (msgId?: string) => {
|
||||||
chatStore.updateCurrentSession((session) => {
|
chatStore.updateCurrentSession(
|
||||||
session.deletedMessageIds &&
|
(session) =>
|
||||||
removeOutdatedEntries(session.deletedMessageIds);
|
(session.messages = session.messages.filter((m) => m.id !== msgId)),
|
||||||
session.messages = session.messages.filter((m) => {
|
);
|
||||||
if (m.id !== msgId) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!session.deletedMessageIds) {
|
|
||||||
session.deletedMessageIds = {} as Record<string, number>;
|
|
||||||
}
|
|
||||||
session.deletedMessageIds[m.id] = Date.now();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDelete = (msgId: string) => {
|
const onDelete = (msgId: string) => {
|
||||||
|
|||||||
@@ -360,21 +360,6 @@ function SyncConfigModal(props: { onClose?: () => void }) {
|
|||||||
</select>
|
</select>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem
|
|
||||||
title={Locale.Settings.Sync.Config.EnableAutoSync.Title}
|
|
||||||
subTitle={Locale.Settings.Sync.Config.EnableAutoSync.SubTitle}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={syncStore.enableAutoSync}
|
|
||||||
onChange={(e) => {
|
|
||||||
syncStore.update(
|
|
||||||
(config) => (config.enableAutoSync = e.currentTarget.checked),
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
></input>
|
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<ListItem
|
<ListItem
|
||||||
title={Locale.Settings.Sync.Config.Proxy.Title}
|
title={Locale.Settings.Sync.Config.Proxy.Title}
|
||||||
subTitle={Locale.Settings.Sync.Config.Proxy.SubTitle}
|
subTitle={Locale.Settings.Sync.Config.Proxy.SubTitle}
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ const cn = {
|
|||||||
Commands: {
|
Commands: {
|
||||||
new: "新建聊天",
|
new: "新建聊天",
|
||||||
newm: "从面具新建聊天",
|
newm: "从面具新建聊天",
|
||||||
copy: "复制当前聊天",
|
|
||||||
next: "下一个聊天",
|
next: "下一个聊天",
|
||||||
prev: "上一个聊天",
|
prev: "上一个聊天",
|
||||||
clear: "清除上下文",
|
clear: "清除上下文",
|
||||||
@@ -233,10 +232,6 @@ const cn = {
|
|||||||
Title: "同步类型",
|
Title: "同步类型",
|
||||||
SubTitle: "选择喜爱的同步服务器",
|
SubTitle: "选择喜爱的同步服务器",
|
||||||
},
|
},
|
||||||
EnableAutoSync: {
|
|
||||||
Title: "自动同步设置",
|
|
||||||
SubTitle: "在回复完成或删除消息后自动同步数据",
|
|
||||||
},
|
|
||||||
Proxy: {
|
Proxy: {
|
||||||
Title: "启用代理",
|
Title: "启用代理",
|
||||||
SubTitle: "在浏览器中同步时,必须启用代理以避免跨域限制",
|
SubTitle: "在浏览器中同步时,必须启用代理以避免跨域限制",
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ const en: LocaleType = {
|
|||||||
Commands: {
|
Commands: {
|
||||||
new: "Start a new chat",
|
new: "Start a new chat",
|
||||||
newm: "Start a new chat with mask",
|
newm: "Start a new chat with mask",
|
||||||
copy: "Copy the current Chat",
|
|
||||||
next: "Next Chat",
|
next: "Next Chat",
|
||||||
prev: "Previous Chat",
|
prev: "Previous Chat",
|
||||||
clear: "Clear Context",
|
clear: "Clear Context",
|
||||||
@@ -235,11 +234,6 @@ const en: LocaleType = {
|
|||||||
Title: "Sync Type",
|
Title: "Sync Type",
|
||||||
SubTitle: "Choose your favorite sync service",
|
SubTitle: "Choose your favorite sync service",
|
||||||
},
|
},
|
||||||
EnableAutoSync: {
|
|
||||||
Title: "Auto Sync Settings",
|
|
||||||
SubTitle:
|
|
||||||
"Automatically synchronize data after replying or deleting messages",
|
|
||||||
},
|
|
||||||
Proxy: {
|
Proxy: {
|
||||||
Title: "Enable CORS Proxy",
|
Title: "Enable CORS Proxy",
|
||||||
SubTitle: "Enable a proxy to avoid cross-origin restrictions",
|
SubTitle: "Enable a proxy to avoid cross-origin restrictions",
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ export const useAccessStore = createPersistStore(
|
|||||||
})
|
})
|
||||||
.then((res: DangerConfig) => {
|
.then((res: DangerConfig) => {
|
||||||
console.log("[Config] got config from server", res);
|
console.log("[Config] got config from server", res);
|
||||||
set(() => ({ lastUpdateTime: Date.now(), ...res }));
|
set(() => ({ ...res }));
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
console.error("[Config] failed to fetch config");
|
console.error("[Config] failed to fetch config");
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
import {
|
import { getMessageTextContent, trimTopic } from "../utils";
|
||||||
getMessageTextContent,
|
|
||||||
trimTopic,
|
|
||||||
removeOutdatedEntries,
|
|
||||||
} from "../utils";
|
|
||||||
|
|
||||||
import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
|
import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
@@ -33,7 +29,6 @@ import { ModelConfig, ModelType, useAppConfig } from "./config";
|
|||||||
import { useAccessStore } from "./access";
|
import { useAccessStore } from "./access";
|
||||||
import { collectModelsWithDefaultModel } from "../utils/model";
|
import { collectModelsWithDefaultModel } from "../utils/model";
|
||||||
import { createEmptyMask, Mask } from "./mask";
|
import { createEmptyMask, Mask } from "./mask";
|
||||||
import { useSyncStore } from "./sync";
|
|
||||||
|
|
||||||
const localStorage = safeLocalStorage();
|
const localStorage = safeLocalStorage();
|
||||||
|
|
||||||
@@ -85,7 +80,6 @@ export interface ChatSession {
|
|||||||
lastUpdate: number;
|
lastUpdate: number;
|
||||||
lastSummarizeIndex: number;
|
lastSummarizeIndex: number;
|
||||||
clearContextIndex?: number;
|
clearContextIndex?: number;
|
||||||
deletedMessageIds?: Record<string, number>;
|
|
||||||
|
|
||||||
mask: Mask;
|
mask: Mask;
|
||||||
}
|
}
|
||||||
@@ -109,7 +103,6 @@ function createEmptySession(): ChatSession {
|
|||||||
},
|
},
|
||||||
lastUpdate: Date.now(),
|
lastUpdate: Date.now(),
|
||||||
lastSummarizeIndex: 0,
|
lastSummarizeIndex: 0,
|
||||||
deletedMessageIds: {},
|
|
||||||
|
|
||||||
mask: createEmptyMask(),
|
mask: createEmptyMask(),
|
||||||
};
|
};
|
||||||
@@ -195,19 +188,9 @@ function fillTemplateWith(input: string, modelConfig: ModelConfig) {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cloudSyncTimer: any = null;
|
|
||||||
function noticeCloudSync(): void {
|
|
||||||
const syncStore = useSyncStore.getState();
|
|
||||||
cloudSyncTimer && clearTimeout(cloudSyncTimer);
|
|
||||||
cloudSyncTimer = setTimeout(() => {
|
|
||||||
syncStore.autoSync();
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_CHAT_STATE = {
|
const DEFAULT_CHAT_STATE = {
|
||||||
sessions: [createEmptySession()],
|
sessions: [createEmptySession()],
|
||||||
currentSessionIndex: 0,
|
currentSessionIndex: 0,
|
||||||
deletedSessionIds: {} as Record<string, number>,
|
|
||||||
lastInput: "",
|
lastInput: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -257,28 +240,6 @@ export const useChatStore = createPersistStore(
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
copySession() {
|
|
||||||
set((state) => {
|
|
||||||
const { sessions, currentSessionIndex } = state;
|
|
||||||
const emptySession = createEmptySession();
|
|
||||||
|
|
||||||
// copy the session
|
|
||||||
const curSession = JSON.parse(
|
|
||||||
JSON.stringify(sessions[currentSessionIndex]),
|
|
||||||
);
|
|
||||||
curSession.id = emptySession.id;
|
|
||||||
curSession.lastUpdate = emptySession.lastUpdate;
|
|
||||||
|
|
||||||
const newSessions = [...sessions];
|
|
||||||
newSessions.splice(0, 0, curSession);
|
|
||||||
|
|
||||||
return {
|
|
||||||
currentSessionIndex: 0,
|
|
||||||
sessions: newSessions,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
moveSession(from: number, to: number) {
|
moveSession(from: number, to: number) {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const { sessions, currentSessionIndex: oldIndex } = state;
|
const { sessions, currentSessionIndex: oldIndex } = state;
|
||||||
@@ -341,18 +302,7 @@ export const useChatStore = createPersistStore(
|
|||||||
if (!deletedSession) return;
|
if (!deletedSession) return;
|
||||||
|
|
||||||
const sessions = get().sessions.slice();
|
const sessions = get().sessions.slice();
|
||||||
const deletedSessionIds = { ...get().deletedSessionIds };
|
sessions.splice(index, 1);
|
||||||
|
|
||||||
removeOutdatedEntries(deletedSessionIds);
|
|
||||||
|
|
||||||
const hasDelSessions = sessions.splice(index, 1);
|
|
||||||
if (hasDelSessions?.length) {
|
|
||||||
hasDelSessions.forEach((session) => {
|
|
||||||
if (session.messages.length > 0) {
|
|
||||||
deletedSessionIds[session.id] = Date.now();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentIndex = get().currentSessionIndex;
|
const currentIndex = get().currentSessionIndex;
|
||||||
let nextIndex = Math.min(
|
let nextIndex = Math.min(
|
||||||
@@ -369,24 +319,19 @@ export const useChatStore = createPersistStore(
|
|||||||
const restoreState = {
|
const restoreState = {
|
||||||
currentSessionIndex: get().currentSessionIndex,
|
currentSessionIndex: get().currentSessionIndex,
|
||||||
sessions: get().sessions.slice(),
|
sessions: get().sessions.slice(),
|
||||||
deletedSessionIds: get().deletedSessionIds,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
set(() => ({
|
set(() => ({
|
||||||
currentSessionIndex: nextIndex,
|
currentSessionIndex: nextIndex,
|
||||||
sessions,
|
sessions,
|
||||||
deletedSessionIds,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
noticeCloudSync();
|
|
||||||
|
|
||||||
showToast(
|
showToast(
|
||||||
Locale.Home.DeleteToast,
|
Locale.Home.DeleteToast,
|
||||||
{
|
{
|
||||||
text: Locale.Home.Revert,
|
text: Locale.Home.Revert,
|
||||||
onClick() {
|
onClick() {
|
||||||
set(() => restoreState);
|
set(() => restoreState);
|
||||||
noticeCloudSync();
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
5000,
|
5000,
|
||||||
@@ -407,24 +352,6 @@ export const useChatStore = createPersistStore(
|
|||||||
return session;
|
return session;
|
||||||
},
|
},
|
||||||
|
|
||||||
sortSessions() {
|
|
||||||
const currentSession = get().currentSession();
|
|
||||||
const sessions = get().sessions.slice();
|
|
||||||
|
|
||||||
sessions.sort(
|
|
||||||
(a, b) =>
|
|
||||||
new Date(b.lastUpdate).getTime() - new Date(a.lastUpdate).getTime(),
|
|
||||||
);
|
|
||||||
const currentSessionIndex = sessions.findIndex((session) => {
|
|
||||||
return session && currentSession && session.id === currentSession.id;
|
|
||||||
});
|
|
||||||
|
|
||||||
set((state) => ({
|
|
||||||
currentSessionIndex,
|
|
||||||
sessions,
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
onNewMessage(message: ChatMessage) {
|
onNewMessage(message: ChatMessage) {
|
||||||
get().updateCurrentSession((session) => {
|
get().updateCurrentSession((session) => {
|
||||||
session.messages = session.messages.concat();
|
session.messages = session.messages.concat();
|
||||||
@@ -432,8 +359,6 @@ export const useChatStore = createPersistStore(
|
|||||||
});
|
});
|
||||||
get().updateStat(message);
|
get().updateStat(message);
|
||||||
get().summarizeSession();
|
get().summarizeSession();
|
||||||
get().sortSessions();
|
|
||||||
noticeCloudSync();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async onUserInput(content: string, attachImages?: string[]) {
|
async onUserInput(content: string, attachImages?: string[]) {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export const DEFAULT_CONFIG = {
|
|||||||
providerName: "OpenAI" as ServiceProvider,
|
providerName: "OpenAI" as ServiceProvider,
|
||||||
temperature: 0.5,
|
temperature: 0.5,
|
||||||
top_p: 1,
|
top_p: 1,
|
||||||
max_tokens: 4000,
|
max_completions_tokens: 4000,
|
||||||
presence_penalty: 0,
|
presence_penalty: 0,
|
||||||
frequency_penalty: 0,
|
frequency_penalty: 0,
|
||||||
sendMemory: true,
|
sendMemory: true,
|
||||||
@@ -127,7 +127,7 @@ export const ModalConfigValidator = {
|
|||||||
model(x: string) {
|
model(x: string) {
|
||||||
return x as ModelType;
|
return x as ModelType;
|
||||||
},
|
},
|
||||||
max_tokens(x: number) {
|
max_completions_tokens(x: number) {
|
||||||
return limitNumber(x, 0, 512000, 1024);
|
return limitNumber(x, 0, 512000, 1024);
|
||||||
},
|
},
|
||||||
presence_penalty(x: number) {
|
presence_penalty(x: number) {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ export type SyncStore = GetStoreState<typeof useSyncStore>;
|
|||||||
|
|
||||||
const DEFAULT_SYNC_STATE = {
|
const DEFAULT_SYNC_STATE = {
|
||||||
provider: ProviderType.WebDAV,
|
provider: ProviderType.WebDAV,
|
||||||
enableAutoSync: true,
|
|
||||||
useProxy: true,
|
useProxy: true,
|
||||||
proxyUrl: ApiPath.Cors as string,
|
proxyUrl: ApiPath.Cors as string,
|
||||||
|
|
||||||
@@ -44,8 +43,6 @@ const DEFAULT_SYNC_STATE = {
|
|||||||
lastProvider: "",
|
lastProvider: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
let lastSyncTime = 0;
|
|
||||||
|
|
||||||
export const useSyncStore = createPersistStore(
|
export const useSyncStore = createPersistStore(
|
||||||
DEFAULT_SYNC_STATE,
|
DEFAULT_SYNC_STATE,
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
@@ -92,16 +89,6 @@ export const useSyncStore = createPersistStore(
|
|||||||
},
|
},
|
||||||
|
|
||||||
async sync() {
|
async sync() {
|
||||||
if (lastSyncTime && lastSyncTime >= Date.now() - 800) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lastSyncTime = Date.now();
|
|
||||||
|
|
||||||
const enableAutoSync = get().enableAutoSync;
|
|
||||||
if (!enableAutoSync) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const localState = getLocalAppState();
|
const localState = getLocalAppState();
|
||||||
const provider = get().provider;
|
const provider = get().provider;
|
||||||
const config = get()[provider];
|
const config = get()[provider];
|
||||||
@@ -116,7 +103,9 @@ export const useSyncStore = createPersistStore(
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
const parsedRemoteState = JSON.parse(remoteState) as AppState;
|
const parsedRemoteState = JSON.parse(
|
||||||
|
await client.get(config.username),
|
||||||
|
) as AppState;
|
||||||
mergeAppState(localState, parsedRemoteState);
|
mergeAppState(localState, parsedRemoteState);
|
||||||
setLocalAppState(localState);
|
setLocalAppState(localState);
|
||||||
}
|
}
|
||||||
@@ -134,14 +123,6 @@ export const useSyncStore = createPersistStore(
|
|||||||
const client = this.getClient();
|
const client = this.getClient();
|
||||||
return await client.check();
|
return await client.check();
|
||||||
},
|
},
|
||||||
|
|
||||||
async autoSync() {
|
|
||||||
const { lastSyncTime, provider } = get();
|
|
||||||
const syncStore = useSyncStore.getState();
|
|
||||||
if (lastSyncTime && syncStore.cloudSync()) {
|
|
||||||
syncStore.sync();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: StoreKey.Sync,
|
name: StoreKey.Sync,
|
||||||
|
|||||||
13
app/utils.ts
13
app/utils.ts
@@ -274,19 +274,6 @@ export function isDalle3(model: string) {
|
|||||||
return "dall-e-3" === model;
|
return "dall-e-3" === model;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeOutdatedEntries(
|
|
||||||
timeMap: Record<string, number>,
|
|
||||||
): Record<string, number> {
|
|
||||||
const oneMonthAgo = Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
||||||
// Delete data from a month ago
|
|
||||||
Object.keys(timeMap).forEach((id) => {
|
|
||||||
if (timeMap[id] < oneMonthAgo) {
|
|
||||||
delete timeMap[id];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return timeMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function showPlugins(provider: ServiceProvider, model: string) {
|
export function showPlugins(provider: ServiceProvider, model: string) {
|
||||||
if (
|
if (
|
||||||
provider == ServiceProvider.OpenAI ||
|
provider == ServiceProvider.OpenAI ||
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { useMaskStore } from "../store/mask";
|
|||||||
import { usePromptStore } from "../store/prompt";
|
import { usePromptStore } from "../store/prompt";
|
||||||
import { StoreKey } from "../constant";
|
import { StoreKey } from "../constant";
|
||||||
import { merge } from "./merge";
|
import { merge } from "./merge";
|
||||||
import { removeOutdatedEntries } from "@/app/utils";
|
|
||||||
|
|
||||||
type NonFunctionKeys<T> = {
|
type NonFunctionKeys<T> = {
|
||||||
[K in keyof T]: T[K] extends (...args: any[]) => any ? never : K;
|
[K in keyof T]: T[K] extends (...args: any[]) => any ? never : K;
|
||||||
@@ -66,10 +65,7 @@ type StateMerger = {
|
|||||||
const MergeStates: StateMerger = {
|
const MergeStates: StateMerger = {
|
||||||
[StoreKey.Chat]: (localState, remoteState) => {
|
[StoreKey.Chat]: (localState, remoteState) => {
|
||||||
// merge sessions
|
// merge sessions
|
||||||
const currentSession = useChatStore.getState().currentSession();
|
|
||||||
|
|
||||||
const localSessions: Record<string, ChatSession> = {};
|
const localSessions: Record<string, ChatSession> = {};
|
||||||
const localDeletedSessionIds = localState.deletedSessionIds || {};
|
|
||||||
localState.sessions.forEach((s) => (localSessions[s.id] = s));
|
localState.sessions.forEach((s) => (localSessions[s.id] = s));
|
||||||
|
|
||||||
remoteState.sessions.forEach((remoteSession) => {
|
remoteState.sessions.forEach((remoteSession) => {
|
||||||
@@ -79,98 +75,29 @@ const MergeStates: StateMerger = {
|
|||||||
const localSession = localSessions[remoteSession.id];
|
const localSession = localSessions[remoteSession.id];
|
||||||
if (!localSession) {
|
if (!localSession) {
|
||||||
// if remote session is new, just merge it
|
// if remote session is new, just merge it
|
||||||
if (
|
localState.sessions.push(remoteSession);
|
||||||
(localDeletedSessionIds[remoteSession.id] || -1) <
|
|
||||||
remoteSession.lastUpdate
|
|
||||||
) {
|
|
||||||
localState.sessions.push(remoteSession);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// if both have the same session id, merge the messages
|
// if both have the same session id, merge the messages
|
||||||
const localMessageIds = new Set(localSession.messages.map((v) => v.id));
|
const localMessageIds = new Set(localSession.messages.map((v) => v.id));
|
||||||
const localDeletedMessageIds = localSession.deletedMessageIds || {};
|
|
||||||
remoteSession.messages.forEach((m) => {
|
remoteSession.messages.forEach((m) => {
|
||||||
if (!localMessageIds.has(m.id)) {
|
if (!localMessageIds.has(m.id)) {
|
||||||
if (
|
localSession.messages.push(m);
|
||||||
!localDeletedMessageIds[m.id] ||
|
|
||||||
new Date(localDeletedMessageIds[m.id]).toLocaleString() < m.date
|
|
||||||
) {
|
|
||||||
localSession.messages.push(m);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const remoteDeletedMessageIds = remoteSession.deletedMessageIds || {};
|
|
||||||
localSession.messages = localSession.messages.filter((localMessage) => {
|
|
||||||
return (
|
|
||||||
!remoteDeletedMessageIds[localMessage.id] ||
|
|
||||||
new Date(localDeletedMessageIds[localMessage.id]).toLocaleString() <
|
|
||||||
localMessage.date
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// sort local messages with date field in asc order
|
// sort local messages with date field in asc order
|
||||||
localSession.messages.sort(
|
localSession.messages.sort(
|
||||||
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(),
|
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(),
|
||||||
);
|
);
|
||||||
localSession.lastUpdate = Math.max(
|
|
||||||
remoteSession.lastUpdate,
|
|
||||||
localSession.lastUpdate,
|
|
||||||
);
|
|
||||||
|
|
||||||
const deletedMessageIds = {
|
|
||||||
...remoteDeletedMessageIds,
|
|
||||||
...localDeletedMessageIds,
|
|
||||||
};
|
|
||||||
removeOutdatedEntries(deletedMessageIds);
|
|
||||||
localSession.deletedMessageIds = deletedMessageIds;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const remoteDeletedSessionIds = remoteState.deletedSessionIds || {};
|
|
||||||
|
|
||||||
const finalIds: Record<string, any> = {};
|
|
||||||
localState.sessions = localState.sessions.filter((localSession) => {
|
|
||||||
// 去除掉重复的会话
|
|
||||||
if (finalIds[localSession.id]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
finalIds[localSession.id] = true;
|
|
||||||
|
|
||||||
// 去除掉非首个空会话,避免多个空会话在中间,不方便管理
|
|
||||||
if (
|
|
||||||
localSession.messages.length === 0 &&
|
|
||||||
localSession != localState.sessions[0]
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 去除云端删除并且删除时间小于本地修改时间的会话
|
|
||||||
return (
|
|
||||||
(remoteDeletedSessionIds[localSession.id] || -1) <=
|
|
||||||
localSession.lastUpdate
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// sort local sessions with date field in desc order
|
// sort local sessions with date field in desc order
|
||||||
localState.sessions.sort(
|
localState.sessions.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
new Date(b.lastUpdate).getTime() - new Date(a.lastUpdate).getTime(),
|
new Date(b.lastUpdate).getTime() - new Date(a.lastUpdate).getTime(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const deletedSessionIds = {
|
|
||||||
...remoteDeletedSessionIds,
|
|
||||||
...localDeletedSessionIds,
|
|
||||||
};
|
|
||||||
removeOutdatedEntries(deletedSessionIds);
|
|
||||||
localState.deletedSessionIds = deletedSessionIds;
|
|
||||||
|
|
||||||
localState.currentSessionIndex = localState.sessions.findIndex(
|
|
||||||
(session) => {
|
|
||||||
return session && currentSession && session.id === currentSession.id;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return localState;
|
return localState;
|
||||||
},
|
},
|
||||||
[StoreKey.Prompt]: (localState, remoteState) => {
|
[StoreKey.Prompt]: (localState, remoteState) => {
|
||||||
@@ -226,9 +153,9 @@ export function mergeWithUpdate<T extends { lastUpdateTime?: number }>(
|
|||||||
remoteState: T,
|
remoteState: T,
|
||||||
) {
|
) {
|
||||||
const localUpdateTime = localState.lastUpdateTime ?? 0;
|
const localUpdateTime = localState.lastUpdateTime ?? 0;
|
||||||
const remoteUpdateTime = remoteState.lastUpdateTime ?? 1;
|
const remoteUpdateTime = localState.lastUpdateTime ?? 1;
|
||||||
|
|
||||||
if (localUpdateTime >= remoteUpdateTime) {
|
if (localUpdateTime < remoteUpdateTime) {
|
||||||
merge(remoteState, localState);
|
merge(remoteState, localState);
|
||||||
return { ...remoteState };
|
return { ...remoteState };
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user