@lobehub/chat 1.36.30 → 1.36.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/docs/self-hosting/environment-variables/model-provider.mdx +7 -0
- package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +7 -0
- package/docs/self-hosting/server-database/dokploy.zh-CN.mdx +12 -12
- package/package.json +1 -1
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/Content.tsx +3 -9
- package/src/app/(main)/chat/(workspace)/@conversation/features/ThreadHydration.tsx +2 -4
- package/src/app/(main)/chat/@session/features/SessionListContent/DefaultMode.tsx +2 -5
- package/src/app/(main)/discover/(detail)/plugin/[slug]/features/InstallPlugin.tsx +10 -15
- package/src/database/repositories/dataImporter/__tests__/index.test.ts +11 -18
- package/src/database/repositories/dataImporter/index.ts +31 -46
- package/src/database/server/models/__tests__/_test_template.ts +1 -1
- package/src/database/server/models/__tests__/agent.test.ts +1 -1
- package/src/database/server/models/__tests__/asyncTask.test.ts +1 -1
- package/src/database/server/models/__tests__/chunk.test.ts +1 -1
- package/src/database/server/models/__tests__/file.test.ts +1 -1
- package/src/database/server/models/__tests__/knowledgeBase.test.ts +1 -2
- package/src/database/server/models/__tests__/message.test.ts +35 -72
- package/src/database/server/models/__tests__/nextauth.test.ts +1 -1
- package/src/database/server/models/__tests__/session.test.ts +1 -1
- package/src/database/server/models/__tests__/sessionGroup.test.ts +1 -2
- package/src/database/server/models/__tests__/topic.test.ts +1 -1
- package/src/database/server/models/__tests__/user.test.ts +1 -1
- package/src/database/server/models/_template.ts +2 -2
- package/src/database/server/models/agent.ts +17 -25
- package/src/database/server/models/asyncTask.ts +2 -2
- package/src/database/server/models/chunk.ts +14 -14
- package/src/database/server/models/embedding.ts +1 -1
- package/src/database/server/models/file.ts +8 -10
- package/src/database/server/models/knowledgeBase.ts +4 -6
- package/src/database/server/models/message.ts +54 -65
- package/src/database/server/models/plugin.ts +6 -2
- package/src/database/server/models/ragEval/dataset.ts +2 -2
- package/src/database/server/models/ragEval/datasetRecord.ts +3 -8
- package/src/database/server/models/ragEval/evaluation.ts +3 -2
- package/src/database/server/models/ragEval/evaluationRecord.ts +2 -2
- package/src/database/server/models/session.ts +40 -35
- package/src/database/server/models/sessionGroup.ts +4 -4
- package/src/database/server/models/thread.ts +2 -2
- package/src/database/server/models/topic.ts +48 -53
- package/src/database/server/models/user.ts +12 -12
- package/src/features/AgentSetting/AgentPlugin/index.tsx +1 -1
- package/src/features/ChatInput/ActionBar/Tools/Dropdown.tsx +4 -4
- package/src/features/Portal/Thread/Chat/ChatList.tsx +1 -2
- package/src/hooks/useCheckPluginsIsInstalled.ts +10 -0
- package/src/hooks/useFetchInstalledPlugins.ts +10 -0
- package/src/hooks/useFetchMessages.ts +15 -0
- package/src/hooks/useFetchSessions.ts +13 -0
- package/src/hooks/useFetchThreads.ts +11 -0
- package/src/hooks/useFetchTopics.ts +6 -6
- package/src/layout/GlobalProvider/StoreInitialization.tsx +3 -1
- package/src/libs/agent-runtime/utils/streams/azureOpenai.test.ts +0 -1
- package/src/libs/next-auth/adapter/index.ts +1 -1
- package/src/server/routers/lambda/chunk.ts +2 -2
- package/src/services/user/client.ts +2 -2
- package/src/store/agent/slices/chat/action.test.ts +21 -10
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +10 -0
- package/src/store/chat/slices/builtinTool/action.ts +0 -1
- package/src/store/chat/slices/message/action.test.ts +3 -1
- package/src/store/chat/slices/message/action.ts +7 -3
- package/src/store/chat/slices/thread/action.ts +3 -3
- package/src/store/chat/slices/topic/action.test.ts +1 -1
- package/src/store/chat/slices/topic/action.ts +3 -3
- package/src/store/global/selectors.ts +6 -0
- package/src/store/session/slices/session/action.ts +6 -3
- package/src/store/session/slices/sessionGroup/action.test.ts +8 -6
- package/src/store/tool/slices/plugin/action.ts +5 -3
- package/src/store/tool/slices/store/action.ts +4 -3
- package/src/store/user/slices/common/action.test.ts +3 -1
- package/vercel.json +1 -1
@@ -29,46 +29,42 @@ export class TopicModel {
|
|
29
29
|
}
|
30
30
|
// **************** Query *************** //
|
31
31
|
|
32
|
-
async
|
32
|
+
query = async ({ current = 0, pageSize = 9999, sessionId }: QueryTopicParams = {}) => {
|
33
33
|
const offset = current * pageSize;
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
.
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
}
|
55
|
-
|
56
|
-
async findById(id: string) {
|
34
|
+
return this.db
|
35
|
+
.select({
|
36
|
+
createdAt: topics.createdAt,
|
37
|
+
favorite: topics.favorite,
|
38
|
+
historySummary: topics.historySummary,
|
39
|
+
id: topics.id,
|
40
|
+
metadata: topics.metadata,
|
41
|
+
title: topics.title,
|
42
|
+
updatedAt: topics.updatedAt,
|
43
|
+
})
|
44
|
+
.from(topics)
|
45
|
+
.where(and(eq(topics.userId, this.userId), this.matchSession(sessionId)))
|
46
|
+
// In boolean sorting, false is considered "smaller" than true.
|
47
|
+
// So here we use desc to ensure that topics with favorite as true are in front.
|
48
|
+
.orderBy(desc(topics.favorite), desc(topics.updatedAt))
|
49
|
+
.limit(pageSize)
|
50
|
+
.offset(offset);
|
51
|
+
};
|
52
|
+
|
53
|
+
findById = async (id: string) => {
|
57
54
|
return this.db.query.topics.findFirst({
|
58
55
|
where: and(eq(topics.id, id), eq(topics.userId, this.userId)),
|
59
56
|
});
|
60
|
-
}
|
57
|
+
};
|
61
58
|
|
62
|
-
async
|
59
|
+
queryAll = async (): Promise<TopicItem[]> => {
|
63
60
|
return this.db
|
64
61
|
.select()
|
65
62
|
.from(topics)
|
66
63
|
.orderBy(topics.updatedAt)
|
67
|
-
.where(eq(topics.userId, this.userId))
|
68
|
-
|
69
|
-
}
|
64
|
+
.where(eq(topics.userId, this.userId));
|
65
|
+
};
|
70
66
|
|
71
|
-
async
|
67
|
+
queryByKeyword = async (keyword: string, sessionId?: string | null): Promise<TopicItem[]> => {
|
72
68
|
if (!keyword) return [];
|
73
69
|
|
74
70
|
const keywordLowerCase = keyword.toLowerCase();
|
@@ -92,26 +88,25 @@ export class TopicModel {
|
|
92
88
|
),
|
93
89
|
),
|
94
90
|
});
|
95
|
-
}
|
91
|
+
};
|
96
92
|
|
97
|
-
async
|
93
|
+
count = async (): Promise<number> => {
|
98
94
|
const result = await this.db
|
99
95
|
.select({
|
100
|
-
count: count(),
|
96
|
+
count: count(topics.id),
|
101
97
|
})
|
102
98
|
.from(topics)
|
103
|
-
.where(eq(topics.userId, this.userId))
|
104
|
-
.execute();
|
99
|
+
.where(eq(topics.userId, this.userId));
|
105
100
|
|
106
101
|
return result[0].count;
|
107
|
-
}
|
102
|
+
};
|
108
103
|
|
109
104
|
// **************** Create *************** //
|
110
105
|
|
111
|
-
async
|
106
|
+
create = async (
|
112
107
|
{ messages: messageIds, ...params }: CreateTopicParams,
|
113
108
|
id: string = this.genId(),
|
114
|
-
): Promise<TopicItem> {
|
109
|
+
): Promise<TopicItem> => {
|
115
110
|
return this.db.transaction(async (tx) => {
|
116
111
|
// 在 topics 表中插入新的 topic
|
117
112
|
const [topic] = await tx
|
@@ -133,9 +128,9 @@ export class TopicModel {
|
|
133
128
|
|
134
129
|
return topic;
|
135
130
|
});
|
136
|
-
}
|
131
|
+
};
|
137
132
|
|
138
|
-
async
|
133
|
+
batchCreate = async (topicParams: (CreateTopicParams & { id?: string })[]) => {
|
139
134
|
// 开始一个事务
|
140
135
|
return this.db.transaction(async (tx) => {
|
141
136
|
// 在 topics 表中批量插入新的 topics
|
@@ -167,9 +162,9 @@ export class TopicModel {
|
|
167
162
|
|
168
163
|
return createdTopics;
|
169
164
|
});
|
170
|
-
}
|
165
|
+
};
|
171
166
|
|
172
|
-
async
|
167
|
+
duplicate = async (topicId: string, newTitle?: string) => {
|
173
168
|
return this.db.transaction(async (tx) => {
|
174
169
|
// find original topic
|
175
170
|
const originalTopic = await tx.query.topics.findFirst({
|
@@ -217,48 +212,48 @@ export class TopicModel {
|
|
217
212
|
topic: duplicatedTopic,
|
218
213
|
};
|
219
214
|
});
|
220
|
-
}
|
215
|
+
};
|
221
216
|
|
222
217
|
// **************** Delete *************** //
|
223
218
|
|
224
219
|
/**
|
225
220
|
* Delete a session, also delete all messages and topics associated with it.
|
226
221
|
*/
|
227
|
-
async
|
222
|
+
delete = async (id: string) => {
|
228
223
|
return this.db.delete(topics).where(and(eq(topics.id, id), eq(topics.userId, this.userId)));
|
229
|
-
}
|
224
|
+
};
|
230
225
|
|
231
226
|
/**
|
232
227
|
* Deletes multiple topics based on the sessionId.
|
233
228
|
*/
|
234
|
-
async
|
229
|
+
batchDeleteBySessionId = async (sessionId?: string | null) => {
|
235
230
|
return this.db
|
236
231
|
.delete(topics)
|
237
232
|
.where(and(this.matchSession(sessionId), eq(topics.userId, this.userId)));
|
238
|
-
}
|
233
|
+
};
|
239
234
|
|
240
235
|
/**
|
241
236
|
* Deletes multiple topics and all messages associated with them in a transaction.
|
242
237
|
*/
|
243
|
-
async
|
238
|
+
batchDelete = async (ids: string[]) => {
|
244
239
|
return this.db
|
245
240
|
.delete(topics)
|
246
241
|
.where(and(inArray(topics.id, ids), eq(topics.userId, this.userId)));
|
247
|
-
}
|
242
|
+
};
|
248
243
|
|
249
|
-
async
|
244
|
+
deleteAll = async () => {
|
250
245
|
return this.db.delete(topics).where(eq(topics.userId, this.userId));
|
251
|
-
}
|
246
|
+
};
|
252
247
|
|
253
248
|
// **************** Update *************** //
|
254
249
|
|
255
|
-
async
|
250
|
+
update = async (id: string, data: Partial<TopicItem>) => {
|
256
251
|
return this.db
|
257
252
|
.update(topics)
|
258
253
|
.set({ ...data, updatedAt: new Date() })
|
259
254
|
.where(and(eq(topics.id, id), eq(topics.userId, this.userId)))
|
260
255
|
.returning();
|
261
|
-
}
|
256
|
+
};
|
262
257
|
|
263
258
|
// **************** Helper *************** //
|
264
259
|
|
@@ -26,7 +26,7 @@ export class UserModel {
|
|
26
26
|
this.db = db;
|
27
27
|
}
|
28
28
|
|
29
|
-
async
|
29
|
+
getUserState = async () => {
|
30
30
|
const result = await this.db
|
31
31
|
.select({
|
32
32
|
isOnboarded: users.isOnboarded,
|
@@ -81,20 +81,20 @@ export class UserModel {
|
|
81
81
|
settings,
|
82
82
|
userId: this.userId,
|
83
83
|
};
|
84
|
-
}
|
84
|
+
};
|
85
85
|
|
86
|
-
async
|
86
|
+
updateUser = async (value: Partial<UserItem>) => {
|
87
87
|
return this.db
|
88
88
|
.update(users)
|
89
89
|
.set({ ...value, updatedAt: new Date() })
|
90
90
|
.where(eq(users.id, this.userId));
|
91
|
-
}
|
91
|
+
};
|
92
92
|
|
93
|
-
async
|
93
|
+
deleteSetting = async () => {
|
94
94
|
return this.db.delete(userSettings).where(eq(userSettings.id, this.userId));
|
95
|
-
}
|
95
|
+
};
|
96
96
|
|
97
|
-
async
|
97
|
+
updateSetting = async (value: Partial<UserSettings>) => {
|
98
98
|
const { keyVaults, ...res } = value;
|
99
99
|
|
100
100
|
// Encrypt keyVaults
|
@@ -120,9 +120,9 @@ export class UserModel {
|
|
120
120
|
}
|
121
121
|
|
122
122
|
return this.db.update(userSettings).set(newValue).where(eq(userSettings.id, this.userId));
|
123
|
-
}
|
123
|
+
};
|
124
124
|
|
125
|
-
async
|
125
|
+
updatePreference = async (value: Partial<UserPreference>) => {
|
126
126
|
const user = await this.db.query.users.findFirst({ where: eq(users.id, this.userId) });
|
127
127
|
if (!user) return;
|
128
128
|
|
@@ -130,9 +130,9 @@ export class UserModel {
|
|
130
130
|
.update(users)
|
131
131
|
.set({ preference: merge(user.preference, value) })
|
132
132
|
.where(eq(users.id, this.userId));
|
133
|
-
}
|
133
|
+
};
|
134
134
|
|
135
|
-
async
|
135
|
+
updateGuide = async (value: Partial<UserGuide>) => {
|
136
136
|
const user = await this.db.query.users.findFirst({ where: eq(users.id, this.userId) });
|
137
137
|
if (!user) return;
|
138
138
|
|
@@ -141,7 +141,7 @@ export class UserModel {
|
|
141
141
|
.update(users)
|
142
142
|
.set({ preference: { ...prevPreference, guide: merge(prevPreference.guide || {}, value) } })
|
143
143
|
.where(eq(users.id, this.userId));
|
144
|
-
}
|
144
|
+
};
|
145
145
|
|
146
146
|
// Static method
|
147
147
|
|
@@ -11,6 +11,7 @@ import { Center, Flexbox } from 'react-layout-kit';
|
|
11
11
|
import { FORM_STYLE } from '@/const/layoutTokens';
|
12
12
|
import PluginStore from '@/features/PluginStore';
|
13
13
|
import PluginTag from '@/features/PluginStore/PluginItem/PluginTag';
|
14
|
+
import { useFetchInstalledPlugins } from '@/hooks/useFetchInstalledPlugins';
|
14
15
|
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
15
16
|
import { pluginHelpers, useToolStore } from '@/store/tool';
|
16
17
|
import { toolSelectors } from '@/store/tool/selectors';
|
@@ -33,7 +34,6 @@ const AgentPlugin = memo(() => {
|
|
33
34
|
|
34
35
|
const { showDalle } = useServerConfigStore(featureFlagsSelectors);
|
35
36
|
const installedPlugins = useToolStore(toolSelectors.metaList(showDalle), isEqual);
|
36
|
-
const useFetchInstalledPlugins = useToolStore((s) => s.useFetchInstalledPlugins);
|
37
37
|
|
38
38
|
const { isLoading } = useFetchInstalledPlugins();
|
39
39
|
|
@@ -10,6 +10,8 @@ import { Flexbox } from 'react-layout-kit';
|
|
10
10
|
|
11
11
|
import { useWorkspaceModal } from '@/app/(main)/chat/(workspace)/features/useWorkspaceModal';
|
12
12
|
import PluginStore from '@/features/PluginStore';
|
13
|
+
import { useCheckPluginsIsInstalled } from '@/hooks/useCheckPluginsIsInstalled';
|
14
|
+
import { useFetchInstalledPlugins } from '@/hooks/useFetchInstalledPlugins';
|
13
15
|
import { useAgentStore } from '@/store/agent';
|
14
16
|
import { agentSelectors } from '@/store/agent/selectors';
|
15
17
|
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
@@ -109,13 +111,11 @@ const DropdownMenu = memo<PropsWithChildren>(({ children }) => {
|
|
109
111
|
|
110
112
|
const plugins = useAgentStore((s) => agentSelectors.currentAgentPlugins(s));
|
111
113
|
|
112
|
-
const [useFetchPluginStore
|
113
|
-
(s) => [s.useFetchPluginStore, s.useFetchInstalledPlugins, s.useCheckPluginsIsInstalled],
|
114
|
-
);
|
114
|
+
const [useFetchPluginStore] = useToolStore((s) => [s.useFetchPluginStore]);
|
115
115
|
|
116
116
|
useFetchPluginStore();
|
117
117
|
useFetchInstalledPlugins();
|
118
|
-
|
118
|
+
useCheckPluginsIsInstalled(plugins);
|
119
119
|
|
120
120
|
return (
|
121
121
|
<>
|
@@ -2,6 +2,7 @@ import React, { memo, useCallback } from 'react';
|
|
2
2
|
import { Flexbox } from 'react-layout-kit';
|
3
3
|
|
4
4
|
import { SkeletonList, VirtualizedList } from '@/features/Conversation';
|
5
|
+
import { useFetchThreads } from '@/hooks/useFetchThreads';
|
5
6
|
import { useChatStore } from '@/store/chat';
|
6
7
|
import { threadSelectors } from '@/store/chat/selectors';
|
7
8
|
|
@@ -15,8 +16,6 @@ const ChatList = memo(({ mobile }: ChatListProps) => {
|
|
15
16
|
const data = useChatStore(threadSelectors.portalDisplayChatIDs);
|
16
17
|
const isInit = useChatStore((s) => s.threadsInit);
|
17
18
|
|
18
|
-
const useFetchThreads = useChatStore((s) => s.useFetchThreads);
|
19
|
-
|
20
19
|
useFetchThreads();
|
21
20
|
|
22
21
|
const itemContent = useCallback(
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { useGlobalStore } from '@/store/global';
|
2
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
3
|
+
import { useToolStore } from '@/store/tool';
|
4
|
+
|
5
|
+
export const useCheckPluginsIsInstalled = (plugins: string[]) => {
|
6
|
+
const isPgliteInited = useGlobalStore(systemStatusSelectors.isPgliteInited);
|
7
|
+
const checkPluginsIsInstalled = useToolStore((s) => s.useCheckPluginsIsInstalled);
|
8
|
+
|
9
|
+
checkPluginsIsInstalled(isPgliteInited, plugins);
|
10
|
+
};
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { useGlobalStore } from '@/store/global';
|
2
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
3
|
+
import { useToolStore } from '@/store/tool';
|
4
|
+
|
5
|
+
export const useFetchInstalledPlugins = () => {
|
6
|
+
const isPgliteInited = useGlobalStore(systemStatusSelectors.isPgliteInited);
|
7
|
+
const [useFetchInstalledPlugins] = useToolStore((s) => [s.useFetchInstalledPlugins]);
|
8
|
+
|
9
|
+
return useFetchInstalledPlugins(isPgliteInited);
|
10
|
+
};
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { useChatStore } from '@/store/chat';
|
2
|
+
import { useGlobalStore } from '@/store/global';
|
3
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
4
|
+
import { useSessionStore } from '@/store/session';
|
5
|
+
|
6
|
+
export const useFetchMessages = () => {
|
7
|
+
const isPgliteInited = useGlobalStore(systemStatusSelectors.isPgliteInited);
|
8
|
+
const [sessionId] = useSessionStore((s) => [s.activeId]);
|
9
|
+
const [activeTopicId, useFetchMessages] = useChatStore((s) => [
|
10
|
+
s.activeTopicId,
|
11
|
+
s.useFetchMessages,
|
12
|
+
]);
|
13
|
+
|
14
|
+
useFetchMessages(isPgliteInited, sessionId, activeTopicId);
|
15
|
+
};
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { useGlobalStore } from '@/store/global';
|
2
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
3
|
+
import { useSessionStore } from '@/store/session';
|
4
|
+
import { useUserStore } from '@/store/user';
|
5
|
+
import { authSelectors } from '@/store/user/slices/auth/selectors';
|
6
|
+
|
7
|
+
export const useFetchSessions = () => {
|
8
|
+
const isPgliteInited = useGlobalStore(systemStatusSelectors.isPgliteInited);
|
9
|
+
const isLogin = useUserStore(authSelectors.isLogin);
|
10
|
+
const useFetchSessions = useSessionStore((s) => s.useFetchSessions);
|
11
|
+
|
12
|
+
useFetchSessions(isPgliteInited, isLogin);
|
13
|
+
};
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import { useChatStore } from '@/store/chat';
|
2
|
+
import { useGlobalStore } from '@/store/global';
|
3
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
4
|
+
|
5
|
+
export const useFetchThreads = (activeTopicId?: string) => {
|
6
|
+
const isPgliteInited = useGlobalStore(systemStatusSelectors.isPgliteInited);
|
7
|
+
|
8
|
+
const [useFetchThreads] = useChatStore((s) => [s.useFetchThreads]);
|
9
|
+
|
10
|
+
useFetchThreads(isPgliteInited, activeTopicId);
|
11
|
+
};
|
@@ -1,4 +1,7 @@
|
|
1
|
+
import { useFetchThreads } from '@/hooks/useFetchThreads';
|
1
2
|
import { useChatStore } from '@/store/chat';
|
3
|
+
import { useGlobalStore } from '@/store/global';
|
4
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
2
5
|
import { useSessionStore } from '@/store/session';
|
3
6
|
|
4
7
|
/**
|
@@ -6,12 +9,9 @@ import { useSessionStore } from '@/store/session';
|
|
6
9
|
*/
|
7
10
|
export const useFetchTopics = () => {
|
8
11
|
const [sessionId] = useSessionStore((s) => [s.activeId]);
|
9
|
-
const [activeTopicId, useFetchTopics
|
10
|
-
|
11
|
-
s.useFetchTopics,
|
12
|
-
s.useFetchThreads,
|
13
|
-
]);
|
14
|
-
useFetchTopics(sessionId);
|
12
|
+
const [activeTopicId, useFetchTopics] = useChatStore((s) => [s.activeTopicId, s.useFetchTopics]);
|
13
|
+
const isPgliteInited = useGlobalStore(systemStatusSelectors.isPgliteInited);
|
15
14
|
|
15
|
+
useFetchTopics(isPgliteInited, sessionId);
|
16
16
|
useFetchThreads(activeTopicId);
|
17
17
|
};
|
@@ -10,6 +10,7 @@ import { useIsMobile } from '@/hooks/useIsMobile';
|
|
10
10
|
import { useEnabledDataSync } from '@/hooks/useSyncData';
|
11
11
|
import { useAgentStore } from '@/store/agent';
|
12
12
|
import { useGlobalStore } from '@/store/global';
|
13
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
13
14
|
import { useServerConfigStore } from '@/store/serverConfig';
|
14
15
|
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
|
15
16
|
import { useUserStore } from '@/store/user';
|
@@ -50,7 +51,8 @@ const StoreInitialization = memo(() => {
|
|
50
51
|
* But during initialization, the value of `enableAuth` might be incorrect cause of the async fetch.
|
51
52
|
* So we need to use `isSignedIn` only to determine whether request for the default agent config and user state.
|
52
53
|
*/
|
53
|
-
const
|
54
|
+
const isPgliteInited = useGlobalStore(systemStatusSelectors.isPgliteInited);
|
55
|
+
const isLoginOnInit = isPgliteInited && (enableNextAuth ? isSignedIn : isLogin);
|
54
56
|
|
55
57
|
// init inbox agent and default agent config
|
56
58
|
useInitAgentStore(isLoginOnInit, serverConfig.defaultAgent?.config);
|
@@ -4,7 +4,7 @@ import type {
|
|
4
4
|
AdapterUser,
|
5
5
|
VerificationToken,
|
6
6
|
} from '@auth/core/adapters';
|
7
|
-
import { and, eq } from 'drizzle-orm';
|
7
|
+
import { and, eq } from 'drizzle-orm/expressions';
|
8
8
|
import type { NeonDatabase } from 'drizzle-orm/neon-serverless';
|
9
9
|
import { Adapter, AdapterAccount } from 'next-auth/adapters';
|
10
10
|
|
@@ -1,14 +1,14 @@
|
|
1
|
-
import { inArray } from 'drizzle-orm';
|
1
|
+
import { inArray } from 'drizzle-orm/expressions';
|
2
2
|
import { z } from 'zod';
|
3
3
|
|
4
4
|
import { DEFAULT_EMBEDDING_MODEL } from '@/const/settings';
|
5
|
+
import { knowledgeBaseFiles } from '@/database/schemas';
|
5
6
|
import { serverDB } from '@/database/server';
|
6
7
|
import { AsyncTaskModel } from '@/database/server/models/asyncTask';
|
7
8
|
import { ChunkModel } from '@/database/server/models/chunk';
|
8
9
|
import { EmbeddingModel } from '@/database/server/models/embedding';
|
9
10
|
import { FileModel } from '@/database/server/models/file';
|
10
11
|
import { MessageModel } from '@/database/server/models/message';
|
11
|
-
import { knowledgeBaseFiles } from '@/database/schemas';
|
12
12
|
import { ModelProvider } from '@/libs/agent-runtime';
|
13
13
|
import { authedProcedure, router } from '@/libs/trpc';
|
14
14
|
import { keyVaults } from '@/libs/trpc/middleware/keyVaults';
|
@@ -42,8 +42,8 @@ export class ClientService implements IUserService {
|
|
42
42
|
return UserModel.resetSettings();
|
43
43
|
};
|
44
44
|
|
45
|
-
updateAvatar(avatar: string) {
|
46
|
-
|
45
|
+
async updateAvatar(avatar: string) {
|
46
|
+
await UserModel.updateAvatar(avatar);
|
47
47
|
}
|
48
48
|
|
49
49
|
async updatePreference(preference: Partial<UserPreference>) {
|
@@ -3,7 +3,6 @@ import { mutate } from 'swr';
|
|
3
3
|
import { describe, expect, it, vi } from 'vitest';
|
4
4
|
|
5
5
|
import { INBOX_SESSION_ID } from '@/const/session';
|
6
|
-
import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
|
7
6
|
import { globalService } from '@/services/global';
|
8
7
|
import { sessionService } from '@/services/session';
|
9
8
|
import { useAgentStore } from '@/store/agent';
|
@@ -24,7 +23,9 @@ describe('AgentSlice', () => {
|
|
24
23
|
it('should call togglePlugin with the provided id and false', async () => {
|
25
24
|
const { result } = renderHook(() => useAgentStore());
|
26
25
|
const pluginId = 'plugin-id';
|
27
|
-
const togglePluginMock = vi
|
26
|
+
const togglePluginMock = vi
|
27
|
+
.spyOn(result.current, 'togglePlugin')
|
28
|
+
.mockResolvedValue(undefined);
|
28
29
|
|
29
30
|
await act(async () => {
|
30
31
|
await result.current.removePlugin(pluginId);
|
@@ -39,8 +40,9 @@ describe('AgentSlice', () => {
|
|
39
40
|
it('should add plugin id to plugins array if not present and open is true or undefined', async () => {
|
40
41
|
const { result } = renderHook(() => useAgentStore());
|
41
42
|
const pluginId = 'plugin-id';
|
42
|
-
const updateAgentConfigMock = vi
|
43
|
-
|
43
|
+
const updateAgentConfigMock = vi
|
44
|
+
.spyOn(result.current, 'updateAgentConfig')
|
45
|
+
.mockResolvedValue(undefined);
|
44
46
|
// 模拟当前配置不包含插件 ID
|
45
47
|
vi.spyOn(agentSelectors, 'currentAgentConfig').mockReturnValue({ plugins: [] } as any);
|
46
48
|
|
@@ -57,8 +59,9 @@ describe('AgentSlice', () => {
|
|
57
59
|
it('should remove plugin id from plugins array if present and open is false', async () => {
|
58
60
|
const { result } = renderHook(() => useAgentStore());
|
59
61
|
const pluginId = 'plugin-id';
|
60
|
-
const updateAgentConfigMock = vi
|
61
|
-
|
62
|
+
const updateAgentConfigMock = vi
|
63
|
+
.spyOn(result.current, 'updateAgentConfig')
|
64
|
+
.mockResolvedValue(undefined);
|
62
65
|
// 模拟当前配置包含插件 ID
|
63
66
|
vi.spyOn(agentSelectors, 'currentAgentConfig').mockReturnValue({
|
64
67
|
plugins: [pluginId],
|
@@ -75,7 +78,9 @@ describe('AgentSlice', () => {
|
|
75
78
|
it('should not modify plugins array if plugin id is not present and open is false', async () => {
|
76
79
|
const { result } = renderHook(() => useAgentStore());
|
77
80
|
const pluginId = 'plugin-id';
|
78
|
-
const updateAgentConfigMock = vi
|
81
|
+
const updateAgentConfigMock = vi
|
82
|
+
.spyOn(result.current, 'updateAgentConfig')
|
83
|
+
.mockResolvedValue(undefined);
|
79
84
|
|
80
85
|
// 模拟当前配置不包含插件 ID
|
81
86
|
vi.spyOn(agentSelectors, 'currentAgentConfig').mockReturnValue({ plugins: [] } as any);
|
@@ -93,7 +98,9 @@ describe('AgentSlice', () => {
|
|
93
98
|
it('should update global config if current session is inbox session', async () => {
|
94
99
|
const { result } = renderHook(() => useAgentStore());
|
95
100
|
const config = { model: 'gpt-3.5-turbo' };
|
96
|
-
const updateSessionConfigMock = vi
|
101
|
+
const updateSessionConfigMock = vi
|
102
|
+
.spyOn(sessionService, 'updateSessionConfig')
|
103
|
+
.mockResolvedValue(undefined);
|
97
104
|
const refreshMock = vi.spyOn(result.current, 'internal_refreshAgentConfig');
|
98
105
|
|
99
106
|
await act(async () => {
|
@@ -113,7 +120,9 @@ describe('AgentSlice', () => {
|
|
113
120
|
it('should update session config if current session is not inbox session', async () => {
|
114
121
|
const { result } = renderHook(() => useAgentStore());
|
115
122
|
const config = { model: 'gpt-3.5-turbo' };
|
116
|
-
const updateSessionConfigMock = vi
|
123
|
+
const updateSessionConfigMock = vi
|
124
|
+
.spyOn(sessionService, 'updateSessionConfig')
|
125
|
+
.mockResolvedValue(undefined);
|
117
126
|
const refreshMock = vi.spyOn(result.current, 'internal_refreshAgentConfig');
|
118
127
|
|
119
128
|
// 模拟当前会话不是收件箱会话
|
@@ -247,7 +256,9 @@ describe('AgentSlice', () => {
|
|
247
256
|
it('should call sessionService.updateSessionConfig', async () => {
|
248
257
|
const { result } = renderHook(() => useAgentStore());
|
249
258
|
|
250
|
-
const updateSessionConfigMock = vi
|
259
|
+
const updateSessionConfigMock = vi
|
260
|
+
.spyOn(sessionService, 'updateSessionConfig')
|
261
|
+
.mockResolvedValue(undefined);
|
251
262
|
|
252
263
|
await act(async () => {
|
253
264
|
await result.current.internal_updateAgentConfig('test-session-id', { foo: 'bar' } as any);
|
@@ -5,6 +5,7 @@ import { LOADING_FLAT } from '@/const/message';
|
|
5
5
|
import { DEFAULT_AGENT_CHAT_CONFIG, DEFAULT_AGENT_CONFIG } from '@/const/settings';
|
6
6
|
import { chatService } from '@/services/chat';
|
7
7
|
import { messageService } from '@/services/message';
|
8
|
+
import { sessionService } from '@/services/session';
|
8
9
|
import { topicService } from '@/services/topic';
|
9
10
|
import { useAgentStore } from '@/store/agent';
|
10
11
|
import { agentSelectors } from '@/store/agent/selectors';
|
@@ -51,6 +52,15 @@ vi.mock('@/services/chat', async (importOriginal) => {
|
|
51
52
|
},
|
52
53
|
};
|
53
54
|
});
|
55
|
+
vi.mock('@/services/session', async (importOriginal) => {
|
56
|
+
const module = await importOriginal();
|
57
|
+
|
58
|
+
return {
|
59
|
+
sessionService: {
|
60
|
+
updateSession: vi.fn(),
|
61
|
+
},
|
62
|
+
};
|
63
|
+
});
|
54
64
|
|
55
65
|
const realCoreProcessMessage = useChatStore.getState().internal_coreProcessMessage;
|
56
66
|
|
@@ -466,7 +466,9 @@ describe('chatMessage actions', () => {
|
|
466
466
|
// 设置模拟返回值
|
467
467
|
(messageService.getMessages as Mock).mockResolvedValue(messages);
|
468
468
|
|
469
|
-
const { result } = renderHook(() =>
|
469
|
+
const { result } = renderHook(() =>
|
470
|
+
useChatStore().useFetchMessages(true, sessionId, topicId),
|
471
|
+
);
|
470
472
|
|
471
473
|
// 等待异步操作完成
|
472
474
|
await waitFor(() => {
|
@@ -47,7 +47,11 @@ export interface ChatMessageAction {
|
|
47
47
|
modifyMessageContent: (id: string, content: string) => Promise<void>;
|
48
48
|
toggleMessageEditing: (id: string, editing: boolean) => void;
|
49
49
|
// query
|
50
|
-
useFetchMessages: (
|
50
|
+
useFetchMessages: (
|
51
|
+
enable: boolean,
|
52
|
+
sessionId: string,
|
53
|
+
topicId?: string,
|
54
|
+
) => SWRResponse<ChatMessage[]>;
|
51
55
|
copyMessage: (id: string, content: string) => Promise<void>;
|
52
56
|
refreshMessages: () => Promise<void>;
|
53
57
|
|
@@ -220,9 +224,9 @@ export const chatMessage: StateCreator<
|
|
220
224
|
|
221
225
|
await get().internal_updateMessageContent(id, content);
|
222
226
|
},
|
223
|
-
useFetchMessages: (sessionId, activeTopicId) =>
|
227
|
+
useFetchMessages: (enable, sessionId, activeTopicId) =>
|
224
228
|
useClientDataSWR<ChatMessage[]>(
|
225
|
-
[SWR_USE_FETCH_MESSAGES, sessionId, activeTopicId],
|
229
|
+
enable ? [SWR_USE_FETCH_MESSAGES, sessionId, activeTopicId] : null,
|
226
230
|
async ([, sessionId, topicId]: [string, string, string | undefined]) =>
|
227
231
|
messageService.getMessages(sessionId, topicId),
|
228
232
|
{
|
@@ -44,7 +44,7 @@ export interface ChatThreadAction {
|
|
44
44
|
openThreadCreator: (messageId: string) => void;
|
45
45
|
openThreadInPortal: (threadId: string, sourceMessageId: string) => void;
|
46
46
|
closeThreadPortal: () => void;
|
47
|
-
useFetchThreads: (topicId?: string) => SWRResponse<ThreadItem[]>;
|
47
|
+
useFetchThreads: (enable: boolean, topicId?: string) => SWRResponse<ThreadItem[]>;
|
48
48
|
summaryThreadTitle: (threadId: string, messages: ChatMessage[]) => Promise<void>;
|
49
49
|
updateThreadTitle: (id: string, title: string) => Promise<void>;
|
50
50
|
removeThread: (id: string) => Promise<void>;
|
@@ -209,9 +209,9 @@ export const chatThreadMessage: StateCreator<
|
|
209
209
|
return data;
|
210
210
|
},
|
211
211
|
|
212
|
-
useFetchThreads: (topicId) =>
|
212
|
+
useFetchThreads: (enable, topicId) =>
|
213
213
|
useClientDataSWR<ThreadItem[]>(
|
214
|
-
!!topicId && isServerMode ? [SWR_USE_FETCH_THREADS, topicId] : null,
|
214
|
+
enable && !!topicId && isServerMode ? [SWR_USE_FETCH_THREADS, topicId] : null,
|
215
215
|
async ([, topicId]: [string, string]) => threadService.getThreads(topicId),
|
216
216
|
{
|
217
217
|
suspense: true,
|