Compare commits

...

79 Commits

Author SHA1 Message Date
GH Action - Upstream Sync
dfd3d24004 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-10-31 01:09:40 +00:00
Dogtiti
a0fa4d7e72 Merge pull request #5737 from hyiip/claude3.5
Some checks are pending
Run Tests / test (push) Waiting to run
add constant to claude 3.5 sonnet 20241022
2024-10-31 00:13:16 +08:00
Lloyd Zhou
d0bd1bf8fd Merge pull request #5740 from yuxuan-ctrl/main
Some checks are pending
Run Tests / test (push) Waiting to run
feat: 新增阿里系模型代码配置
2024-10-30 16:56:53 +08:00
yuxuan-ctrl
86ffa1e643 feat: 新增阿里系模型代码配置 2024-10-30 16:30:01 +08:00
GH Action - Upstream Sync
7f3ec6d81c Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-10-30 01:09:09 +00:00
hyiip
736cbdbdd1 add constant to claude 3.5 sonnet 20241022 2024-10-30 02:18:41 +08:00
Dogtiti
613d67eada Merge pull request #5729 from ConnectAI-E/feature/jest
Some checks are pending
Run Tests / test (push) Waiting to run
chore: improve jest
2024-10-29 19:39:59 +08:00
Dogtiti
89cea18955 Merge branch 'main' of https://github.com/ConnectAI-E/ChatGPT-Next-Web into feature/jest 2024-10-29 19:26:52 +08:00
GH Action - Upstream Sync
f45a6938e3 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-10-29 01:09:58 +00:00
Dogtiti
56bc77d20b Merge pull request #5731 from ChatGPTNextWeb/dependabot/npm_and_yarn/testing-library/jest-dom-6.6.2
Some checks are pending
Run Tests / test (push) Waiting to run
Bump @testing-library/jest-dom from 6.4.8 to 6.6.2
2024-10-28 21:52:08 +08:00
Dogtiti
6d93d37963 Merge pull request #5732 from ChatGPTNextWeb/dependabot/npm_and_yarn/types/jest-29.5.14
Bump @types/jest from 29.5.13 to 29.5.14
2024-10-28 21:51:59 +08:00
dependabot[bot]
24df85cf9d Bump @types/jest from 29.5.13 to 29.5.14
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.5.13 to 29.5.14.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 10:31:34 +00:00
dependabot[bot]
a4d7a2c6e3 Bump @testing-library/jest-dom from 6.4.8 to 6.6.2
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 6.4.8 to 6.6.2.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v6.4.8...v6.6.2)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 10:31:27 +00:00
Dogtiti
49d42bb45d chore: improve jest 2024-10-28 16:47:05 +08:00
GH Action - Upstream Sync
3e02a71e0d Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-10-27 01:13:27 +00:00
Lloyd Zhou
4f49626303 Merge pull request #5722 from ElricLiu/main
Some checks failed
Run Tests / test (push) Has been cancelled
Update README.md
2024-10-26 12:09:09 +08:00
ElricLiu
45db20c1c3 Update README.md 2024-10-26 11:16:43 +08:00
Lloyd Zhou
82994843f5 Merge pull request #5719 from ConnectAI-E/hotfix/status_text_error
Some checks are pending
Run Tests / test (push) Waiting to run
hotfix for statusText is non ISO-8859-1 #5717
2024-10-25 20:34:15 +08:00
Dogtiti
1110a087a0 Merge pull request #5720 from ConnectAI-E/hotfix/gemini_invald_argument
hotfix for gemini invald argument #5715
2024-10-25 18:25:46 +08:00
lloydzhou
f0b3e10a6c hotfix for gemini invald argument #5715 2024-10-25 18:19:22 +08:00
lloydzhou
f89872b833 hotfix for gemini invald argument #5715 2024-10-25 18:12:09 +08:00
lloydzhou
90ced92876 update 2024-10-25 18:05:29 +08:00
lloydzhou
2c74559010 hitfix 2024-10-25 18:02:51 +08:00
lloydzhou
e3ca7e8b44 hotfix for statusText is non ISO-8859-1 #5717 2024-10-25 17:52:08 +08:00
GH Action - Upstream Sync
f80da8a263 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-10-11 01:07:41 +00:00
GH Action - Upstream Sync
98ab561607 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-10-10 01:07:14 +00:00
织梦人
9a025ae196 Merge tag 'v2.15.4' 2024-10-09 19:08:43 +08:00
GH Action - Upstream Sync
c4ae73d8a1 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-10-09 01:07:24 +00:00
GH Action - Upstream Sync
31900cbff3 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-10-07 01:10:00 +00:00
GH Action - Upstream Sync
c6657d3d0c Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-10-04 01:07:46 +00:00
GH Action - Upstream Sync
cf7c6f2b9a Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-10-01 01:14:19 +00:00
GH Action - Upstream Sync
41242cae9b Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-09-30 01:10:08 +00:00
GH Action - Upstream Sync
89edebd93c Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-09-28 01:07:15 +00:00
织梦人
60bd3c56c1 Merge remote-tracking branch 'up/main'
# Conflicts:
#	app/store/chat.ts
2024-09-26 20:47:29 +08:00
GH Action - Upstream Sync
659a389fd4 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-09-14 01:03:57 +00:00
GH Action - Upstream Sync
144fdc9b7c Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-09-13 01:04:37 +00:00
织梦人
35f52886c4 Merge remote-tracking branch 'up/main'
# Conflicts:
#	app/store/chat.ts
2024-09-12 09:19:04 +08:00
织梦人
9551f5dfc6 Merge branch 'website'
# Conflicts:
#	app/components/chat.tsx
#	app/utils.ts
#	app/utils/sync.ts
2024-09-07 13:00:33 +08:00
织梦人
370ce3eeca Merge remote-tracking branch 'up/website' into website
# Conflicts:
#	app/components/chat.tsx
#	app/utils.ts
2024-09-07 12:51:37 +08:00
织梦人
5ae4921ee0 fix: 优化云同步功能,自动去除掉非首个空会话,避免多个空会话在中间,更方便管理 2024-09-06 20:59:53 +08:00
lloydzhou
6f3d7530b9 Merge remote-tracking branch 'origin/main' into website 2024-09-06 20:18:21 +08:00
织梦人
6dc868154d fix: 优化云同步功能,使access配置按更新时间合并,解决自定义模型配置在同步后丢失的问题 2024-09-05 21:52:25 +08:00
织梦人
ccacfec918 feat: 优化聊天窗口,使支持复制会话 2024-09-05 21:19:03 +08:00
GH Action - Upstream Sync
c204031ea7 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-09-05 01:04:08 +00:00
GH Action - Upstream Sync
2bf72d0324 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-08-30 01:03:42 +00:00
GH Action - Upstream Sync
e8c7ac0c45 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-08-28 01:02:59 +00:00
GH Action - Upstream Sync
0638db146e Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-08-25 01:06:24 +00:00
GH Action - Upstream Sync
2d68f179d7 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-08-22 01:02:16 +00:00
GH Action - Upstream Sync
f1d69cb312 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-08-21 01:00:45 +00:00
李超
31baa10363 fix: 解决会话列表按最新操作时间倒序排序,当前会话判断失败的bug 2024-08-20 22:23:17 +08:00
李超
2fdb35bcc8 fix: 解决会话列表按最新操作时间倒序排序,当前会话判断失败的bug 2024-08-20 22:21:38 +08:00
李超
5c51fd2ed8 feat: 优化会话列表按最后更新时间倒序排序,更方便查看与管理 2024-08-20 21:19:31 +08:00
李超
d0b7ddc1d6 feat: 优化会话列表按最后更新时间倒序排序,更方便查看与管理 2024-08-20 21:18:28 +08:00
GH Action - Upstream Sync
0745b6498d Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-08-20 01:00:40 +00:00
织梦人
fc97c4b06f 更新docker.yml,使image名自适应,不影响主仓库
(cherry picked from commit fdb89af355)
2024-08-18 21:20:20 +08:00
织梦人
fdb89af355 更新docker.yml,使image名自适应,不影响主仓库 2024-08-18 21:19:06 +08:00
织梦人
e515f0f957 更新docker.yml, 修改自动编译的镜像为自己的账号
(cherry picked from commit b2336f5ed9)
2024-08-18 20:23:18 +08:00
织梦人
31f282970b Merge remote-tracking branch 'up/website' into website 2024-08-18 20:22:30 +08:00
织梦人
b2336f5ed9 更新docker.yml, 修改自动编译的镜像为自己的账号 2024-08-18 19:55:55 +08:00
GH Action - Upstream Sync
0a6ddda992 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-08-17 00:59:04 +00:00
lloydzhou
5e1064a5c8 Merge branch 'main' into website 2024-08-16 16:58:30 +08:00
李超
2ee2d50ae6 Merge remote-tracking branch 'up/website' into website 2024-08-15 22:59:03 +08:00
李超
eae593d660 feat: Add automatic data synchronization settings and implementation, enabling auto-sync after completing replies or deleting conversations 2024-08-15 22:42:23 +08:00
织梦人
621b1480c2 Merge branch 'ChatGPTNextWeb:main' into main 2024-08-15 22:41:31 +08:00
李超
4b22aaf979 feat: Add automatic data synchronization settings and implementation, enabling auto-sync after completing replies or deleting conversations 2024-08-15 22:39:30 +08:00
GH Action - Upstream Sync
93bfb55822 Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2024-08-14 01:01:30 +00:00
李超
648e60028d feat: The cloud synchronization feature is enhanced to support the synchronization of deleted conversations and deleted messages 2024-08-08 22:02:15 +08:00
李超
4f876f3e65 Merge tag 'v2.14.1' into website
# Conflicts:
#	app/components/chat.tsx
#	app/utils.ts
2024-08-08 12:48:36 +08:00
lloydzhou
faac0d9817 Merge remote-tracking branch 'origin/main' into website 2024-08-06 22:45:16 +08:00
李超
22c79595fb feat: The cloud synchronization feature is enhanced to support the synchronization of deleted conversations and deleted messages 2024-08-03 20:53:36 +08:00
李超
5065091b74 fix: Fixed the issue that WebDAV synchronization could not check the status and failed during the first backup
(cherry picked from commit 716899c030)
2024-08-03 12:41:36 +08:00
李超
22f61295bc fix: Fixed an issue where the sample of the reply content was displayed out of order
(cherry picked from commit 8498cadae8)
2024-08-03 12:41:36 +08:00
lloydzhou
c440637ad0 Merge remote-tracking branch 'origin/main' into website 2024-07-27 01:32:47 +08:00
lloydzhou
284d33bcdf Merge remote-tracking branch 'origin/main' into website 2024-07-19 18:37:32 +08:00
lloydzhou
d9573973ca Merge remote-tracking branch 'origin/main' into website 2024-07-13 21:31:15 +08:00
fred-bf
cd354cf045 Merge pull request #4685 from ChatGPTNextWeb/main
feat: update upstream
2024-05-14 17:40:46 +08:00
fred-bf
1cce87acaa Merge pull request #4181 from ChatGPTNextWeb/main
merge main
2024-03-01 11:10:11 +08:00
fred-bf
78c4084501 Merge pull request #4148 from ChatGPTNextWeb/main
feat: catch up latest commit
2024-02-27 10:43:15 +08:00
Fred Liang
1d0a40b9e8 chore: low the google safety setting to avoid unexpected blocking 2023-12-31 19:50:06 +08:00
20 changed files with 361 additions and 50 deletions

View File

@@ -19,26 +19,26 @@ jobs:
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
-
name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: yidadaa/chatgpt-next-web
images: ${{ secrets.DOCKER_USERNAME }}/chatgpt-next-web
tags: |
type=raw,value=latest
type=ref,event=tag
-
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
-
name: Build and push Docker image
uses: docker/build-push-action@v4
with:
@@ -49,4 +49,4 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

View File

@@ -31,7 +31,7 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4
[MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple
[Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu
[<img src="https://vercel.com/button" alt="Deploy on Zeabur" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
[<img src="https://vercel.com/button" alt="Deploy on Zeabur" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) [<img src="https://img.shields.io/badge/BT_Deploy-Install-20a53a" alt="Open in Gitpod" height="30">](https://www.bt.cn/new/download.html)
[<img src="https://github.com/user-attachments/assets/903482d4-3e87-4134-9af1-f2588fa90659" height="60" width="288" >](https://monica.im/?utm=nxcrp)

View File

@@ -192,7 +192,10 @@ export class GeminiProApi implements LLMApi {
requestPayload,
getHeaders(),
// @ts-ignore
[{ functionDeclarations: tools.map((tool) => tool.function) }],
tools.length > 0
? // @ts-ignore
[{ functionDeclarations: tools.map((tool) => tool.function) }]
: [],
funcs,
controller,
// parseSSE

View File

@@ -35,6 +35,7 @@ export function useCommand(commands: Commands = {}) {
interface ChatCommands {
new?: Command;
newm?: Command;
copy?: Command;
next?: Command;
prev?: Command;
clear?: Command;

View File

@@ -70,6 +70,7 @@ import {
getMessageImages,
isVisionModel,
isDalle3,
removeOutdatedEntries,
showPlugins,
safeLocalStorage,
} from "../utils";
@@ -987,6 +988,7 @@ function _Chat() {
const chatCommands = useChatCommand({
new: () => chatStore.newSession(),
newm: () => navigate(Path.NewChat),
copy: () => chatStore.copySession(),
prev: () => chatStore.nextSession(-1),
next: () => chatStore.nextSession(1),
clear: () =>
@@ -1118,10 +1120,20 @@ function _Chat() {
};
const deleteMessage = (msgId?: string) => {
chatStore.updateCurrentSession(
(session) =>
(session.messages = session.messages.filter((m) => m.id !== msgId)),
);
chatStore.updateCurrentSession((session) => {
session.deletedMessageIds &&
removeOutdatedEntries(session.deletedMessageIds);
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) => {

View File

@@ -361,6 +361,21 @@ function SyncConfigModal(props: { onClose?: () => void }) {
</select>
</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
title={Locale.Settings.Sync.Config.Proxy.Title}
subTitle={Locale.Settings.Sync.Config.Proxy.SubTitle}

View File

@@ -319,6 +319,7 @@ const anthropicModels = [
"claude-3-opus-20240229",
"claude-3-haiku-20240307",
"claude-3-5-sonnet-20240620",
"claude-3-5-sonnet-20241022",
];
const baiduModels = [

View File

@@ -62,6 +62,7 @@ const cn = {
Commands: {
new: "新建聊天",
newm: "从面具新建聊天",
copy: "复制当前聊天",
next: "下一个聊天",
prev: "上一个聊天",
clear: "清除上下文",
@@ -234,6 +235,10 @@ const cn = {
Title: "同步类型",
SubTitle: "选择喜爱的同步服务器",
},
EnableAutoSync: {
Title: "自动同步设置",
SubTitle: "在回复完成或删除消息后自动同步数据",
},
Proxy: {
Title: "启用代理",
SubTitle: "在浏览器中同步时,必须启用代理以避免跨域限制",

View File

@@ -63,6 +63,7 @@ const en: LocaleType = {
Commands: {
new: "Start a new chat",
newm: "Start a new chat with mask",
copy: "Copy the current Chat",
next: "Next Chat",
prev: "Previous Chat",
clear: "Clear Context",
@@ -236,6 +237,11 @@ const en: LocaleType = {
Title: "Sync Type",
SubTitle: "Choose your favorite sync service",
},
EnableAutoSync: {
Title: "Auto Sync Settings",
SubTitle:
"Automatically synchronize data after replying or deleting messages",
},
Proxy: {
Title: "Enable CORS Proxy",
SubTitle: "Enable a proxy to avoid cross-origin restrictions",

View File

@@ -223,7 +223,7 @@ export const useAccessStore = createPersistStore(
})
.then((res: DangerConfig) => {
console.log("[Config] got config from server", res);
set(() => ({ ...res }));
set(() => ({ lastUpdateTime: Date.now(), ...res }));
})
.catch(() => {
console.error("[Config] failed to fetch config");

View File

@@ -1,4 +1,8 @@
import { getMessageTextContent, trimTopic } from "../utils";
import {
getMessageTextContent,
trimTopic,
removeOutdatedEntries,
} from "../utils";
import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
import { nanoid } from "nanoid";
@@ -29,6 +33,7 @@ import { ModelConfig, ModelType, useAppConfig } from "./config";
import { useAccessStore } from "./access";
import { collectModelsWithDefaultModel } from "../utils/model";
import { createEmptyMask, Mask } from "./mask";
import { useSyncStore } from "./sync";
const localStorage = safeLocalStorage();
@@ -80,6 +85,7 @@ export interface ChatSession {
lastUpdate: number;
lastSummarizeIndex: number;
clearContextIndex?: number;
deletedMessageIds?: Record<string, number>;
mask: Mask;
}
@@ -103,6 +109,7 @@ function createEmptySession(): ChatSession {
},
lastUpdate: Date.now(),
lastSummarizeIndex: 0,
deletedMessageIds: {},
mask: createEmptyMask(),
};
@@ -188,9 +195,19 @@ function fillTemplateWith(input: string, modelConfig: ModelConfig) {
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 = {
sessions: [createEmptySession()],
currentSessionIndex: 0,
deletedSessionIds: {} as Record<string, number>,
lastInput: "",
};
@@ -240,6 +257,28 @@ 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) {
set((state) => {
const { sessions, currentSessionIndex: oldIndex } = state;
@@ -302,7 +341,18 @@ export const useChatStore = createPersistStore(
if (!deletedSession) return;
const sessions = get().sessions.slice();
sessions.splice(index, 1);
const deletedSessionIds = { ...get().deletedSessionIds };
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;
let nextIndex = Math.min(
@@ -319,19 +369,24 @@ export const useChatStore = createPersistStore(
const restoreState = {
currentSessionIndex: get().currentSessionIndex,
sessions: get().sessions.slice(),
deletedSessionIds: get().deletedSessionIds,
};
set(() => ({
currentSessionIndex: nextIndex,
sessions,
deletedSessionIds,
}));
noticeCloudSync();
showToast(
Locale.Home.DeleteToast,
{
text: Locale.Home.Revert,
onClick() {
set(() => restoreState);
noticeCloudSync();
},
},
5000,
@@ -352,6 +407,24 @@ export const useChatStore = createPersistStore(
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) {
get().updateCurrentSession((session) => {
session.messages = session.messages.concat();
@@ -359,6 +432,8 @@ export const useChatStore = createPersistStore(
});
get().updateStat(message);
get().summarizeSession();
get().sortSessions();
noticeCloudSync();
},
async onUserInput(content: string, attachImages?: string[]) {

View File

@@ -24,6 +24,7 @@ export type SyncStore = GetStoreState<typeof useSyncStore>;
const DEFAULT_SYNC_STATE = {
provider: ProviderType.WebDAV,
enableAutoSync: true,
useProxy: true,
proxyUrl: ApiPath.Cors as string,
@@ -43,6 +44,8 @@ const DEFAULT_SYNC_STATE = {
lastProvider: "",
};
let lastSyncTime = 0;
export const useSyncStore = createPersistStore(
DEFAULT_SYNC_STATE,
(set, get) => ({
@@ -89,6 +92,16 @@ export const useSyncStore = createPersistStore(
},
async sync() {
if (lastSyncTime && lastSyncTime >= Date.now() - 800) {
return;
}
lastSyncTime = Date.now();
const enableAutoSync = get().enableAutoSync;
if (!enableAutoSync) {
return;
}
const localState = getLocalAppState();
const provider = get().provider;
const config = get()[provider];
@@ -103,9 +116,7 @@ export const useSyncStore = createPersistStore(
);
return;
} else {
const parsedRemoteState = JSON.parse(
await client.get(config.username),
) as AppState;
const parsedRemoteState = JSON.parse(remoteState) as AppState;
mergeAppState(localState, parsedRemoteState);
setLocalAppState(localState);
}
@@ -123,6 +134,14 @@ export const useSyncStore = createPersistStore(
const client = this.getClient();
return await client.check();
},
async autoSync() {
const { lastSyncTime, provider } = get();
const syncStore = useSyncStore.getState();
if (lastSyncTime && syncStore.cloudSync()) {
syncStore.sync();
}
},
}),
{
name: StoreKey.Sync,

View File

@@ -274,6 +274,19 @@ export function isDalle3(model: string) {
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) {
if (
provider == ServiceProvider.OpenAI ||

View File

@@ -100,7 +100,8 @@ export function fetch(url: string, options?: RequestInit): Promise<any> {
})
.catch((e) => {
console.error("stream error", e);
throw e;
// throw e;
return new Response("", { status: 599 });
});
}
return window.fetch(url, options);

View File

@@ -8,6 +8,7 @@ import { useMaskStore } from "../store/mask";
import { usePromptStore } from "../store/prompt";
import { StoreKey } from "../constant";
import { merge } from "./merge";
import { removeOutdatedEntries } from "@/app/utils";
type NonFunctionKeys<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? never : K;
@@ -65,7 +66,10 @@ type StateMerger = {
const MergeStates: StateMerger = {
[StoreKey.Chat]: (localState, remoteState) => {
// merge sessions
const currentSession = useChatStore.getState().currentSession();
const localSessions: Record<string, ChatSession> = {};
const localDeletedSessionIds = localState.deletedSessionIds || {};
localState.sessions.forEach((s) => (localSessions[s.id] = s));
remoteState.sessions.forEach((remoteSession) => {
@@ -75,29 +79,98 @@ const MergeStates: StateMerger = {
const localSession = localSessions[remoteSession.id];
if (!localSession) {
// if remote session is new, just merge it
localState.sessions.push(remoteSession);
if (
(localDeletedSessionIds[remoteSession.id] || -1) <
remoteSession.lastUpdate
) {
localState.sessions.push(remoteSession);
}
} else {
// if both have the same session id, merge the messages
const localMessageIds = new Set(localSession.messages.map((v) => v.id));
const localDeletedMessageIds = localSession.deletedMessageIds || {};
remoteSession.messages.forEach((m) => {
if (!localMessageIds.has(m.id)) {
localSession.messages.push(m);
if (
!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
localSession.messages.sort(
(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
localState.sessions.sort(
(a, b) =>
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;
},
[StoreKey.Prompt]: (localState, remoteState) => {
@@ -153,9 +226,9 @@ export function mergeWithUpdate<T extends { lastUpdateTime?: number }>(
remoteState: T,
) {
const localUpdateTime = localState.lastUpdateTime ?? 0;
const remoteUpdateTime = localState.lastUpdateTime ?? 1;
const remoteUpdateTime = remoteState.lastUpdateTime ?? 1;
if (localUpdateTime < remoteUpdateTime) {
if (localUpdateTime >= remoteUpdateTime) {
merge(remoteState, localState);
return { ...remoteState };
} else {

View File

@@ -1,2 +1,24 @@
// Learn more: https://github.com/testing-library/jest-dom
import "@testing-library/jest-dom";
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
status: 200,
json: () => Promise.resolve({}),
headers: new Headers(),
redirected: false,
statusText: "OK",
type: "basic",
url: "",
clone: function () {
return this;
},
body: null,
bodyUsed: false,
arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)),
blob: () => Promise.resolve(new Blob()),
formData: () => Promise.resolve(new FormData()),
text: () => Promise.resolve(""),
}),
);

View File

@@ -94,8 +94,12 @@ if (mode !== "export") {
source: "/sharegpt",
destination: "https://sharegpt.com/api/conversations",
},
{
source: "/api/proxy/alibaba/:path*",
destination: "https://dashscope.aliyuncs.com/api/:path*",
},
];
return {
beforeFiles: ret,
};

View File

@@ -33,8 +33,8 @@
"html-to-image": "^1.11.11",
"idb-keyval": "^6.2.1",
"lodash-es": "^4.17.21",
"mermaid": "^10.6.1",
"markdown-to-txt": "^2.0.1",
"mermaid": "^10.6.1",
"nanoid": "^5.0.3",
"next": "^14.1.1",
"node-fetch": "^3.3.1",
@@ -56,9 +56,10 @@
"devDependencies": {
"@tauri-apps/api": "^1.6.0",
"@tauri-apps/cli": "1.5.11",
"@testing-library/jest-dom": "^6.4.8",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.2",
"@testing-library/react": "^16.0.0",
"@types/jest": "^29.5.13",
"@types/jest": "^29.5.14",
"@types/js-yaml": "4.0.9",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.11.30",

View File

@@ -119,11 +119,22 @@ pub async fn stream_fetch(
}
}
Err(err) => {
println!("Error response: {:?}", err.source().expect("REASON").to_string());
let error: String = err.source()
.map(|e| e.to_string())
.unwrap_or_else(|| "Unknown error occurred".to_string());
println!("Error response: {:?}", error);
tauri::async_runtime::spawn( async move {
if let Err(e) = window.emit(event_name, ChunkPayload{ request_id, chunk: error.into() }) {
println!("Failed to emit chunk payload: {:?}", e);
}
if let Err(e) = window.emit(event_name, EndPayload{ request_id, status: 0 }) {
println!("Failed to emit end payload: {:?}", e);
}
});
StreamResponse {
request_id,
status: 599,
status_text: err.source().expect("REASON").to_string(),
status_text: "Error".to_string(),
headers: HashMap::new(),
}
}

View File

@@ -27,6 +27,15 @@
dependencies:
"@babel/highlight" "^7.18.6"
"@babel/code-frame@^7.10.4":
version "7.26.0"
resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.26.0.tgz#9374b5cd068d128dac0b94ff482594273b1c2815"
integrity sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==
dependencies:
"@babel/helper-validator-identifier" "^7.25.9"
js-tokens "^4.0.0"
picocolors "^1.0.0"
"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.24.7":
version "7.24.7"
resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465"
@@ -394,6 +403,11 @@
resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db"
integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==
"@babel/helper-validator-identifier@^7.25.9":
version "7.25.9"
resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0":
version "7.21.0"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180"
@@ -2093,13 +2107,26 @@
"@tauri-apps/cli-win32-ia32-msvc" "1.5.11"
"@tauri-apps/cli-win32-x64-msvc" "1.5.11"
"@testing-library/jest-dom@^6.4.8":
version "6.4.8"
resolved "https://registry.npmmirror.com/@testing-library/jest-dom/-/jest-dom-6.4.8.tgz#9c435742b20c6183d4e7034f2b329d562c079daa"
integrity sha512-JD0G+Zc38f5MBHA4NgxQMR5XtO5Jx9g86jqturNTt2WUfRmLDIY7iKkWHDCCTiDuFMre6nxAD5wHw9W5kI4rGw==
"@testing-library/dom@^10.4.0":
version "10.4.0"
resolved "https://registry.npmmirror.com/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8"
integrity sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==
dependencies:
"@babel/code-frame" "^7.10.4"
"@babel/runtime" "^7.12.5"
"@types/aria-query" "^5.0.1"
aria-query "5.3.0"
chalk "^4.1.0"
dom-accessibility-api "^0.5.9"
lz-string "^1.5.0"
pretty-format "^27.0.2"
"@testing-library/jest-dom@^6.6.2":
version "6.6.2"
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.6.2.tgz#8186aa9a07263adef9cc5a59a4772db8c31f4a5b"
integrity sha512-P6GJD4yqc9jZLbe98j/EkyQDTPgqftohZF5FBkHY5BUERZmcf4HeO2k0XaefEg329ux2p21i1A1DmyQ1kKw2Jw==
dependencies:
"@adobe/css-tools" "^4.4.0"
"@babel/runtime" "^7.9.2"
aria-query "^5.0.0"
chalk "^3.0.0"
css.escape "^1.5.1"
@@ -2144,6 +2171,11 @@
resolved "https://registry.npmmirror.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
"@types/aria-query@^5.0.1":
version "5.0.4"
resolved "https://registry.npmmirror.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708"
integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==
"@types/babel__core@^7.1.14":
version "7.20.5"
resolved "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
@@ -2263,10 +2295,10 @@
dependencies:
"@types/istanbul-lib-report" "*"
"@types/jest@^29.5.13":
version "29.5.13"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.13.tgz#8bc571659f401e6a719a7bf0dbcb8b78c71a8adc"
integrity sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==
"@types/jest@^29.5.14":
version "29.5.14"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5"
integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==
dependencies:
expect "^29.0.0"
pretty-format "^29.0.0"
@@ -2738,20 +2770,13 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
aria-query@^5.0.0:
aria-query@5.3.0, aria-query@^5.0.0, aria-query@^5.1.3:
version "5.3.0"
resolved "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e"
integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==
dependencies:
dequal "^2.0.3"
aria-query@^5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e"
integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==
dependencies:
deep-equal "^2.0.5"
array-buffer-byte-length@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead"
@@ -3081,7 +3106,7 @@ chalk@^3.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@^4.0.0, chalk@^4.1.2:
chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -3877,6 +3902,11 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
dom-accessibility-api@^0.5.9:
version "0.5.16"
resolved "https://registry.npmmirror.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453"
integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==
dom-accessibility-api@^0.6.3:
version "0.6.3"
resolved "https://registry.npmmirror.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8"
@@ -6052,6 +6082,11 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
lz-string@^1.5.0:
version "1.5.0"
resolved "https://registry.npmmirror.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
make-dir@^4.0.0:
version "4.0.0"
resolved "https://registry.npmmirror.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e"
@@ -7018,6 +7053,15 @@ prettier@^3.0.2:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.2.tgz#78fcecd6d870551aa5547437cdae39d4701dca5b"
integrity sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==
pretty-format@^27.0.2:
version "27.5.1"
resolved "https://registry.npmmirror.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
dependencies:
ansi-regex "^5.0.1"
ansi-styles "^5.0.0"
react-is "^17.0.1"
pretty-format@^29.0.0, pretty-format@^29.7.0:
version "29.7.0"
resolved "https://registry.npmmirror.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"
@@ -7109,6 +7153,11 @@ react-is@^16.13.1, react-is@^16.7.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^17.0.1:
version "17.0.2"
resolved "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-is@^18.0.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"