@lobehub/chat 1.36.31 → 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.
Files changed (42) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/changelog/v1.json +9 -0
  3. package/docs/self-hosting/environment-variables/model-provider.mdx +7 -0
  4. package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +7 -0
  5. package/docs/self-hosting/server-database/dokploy.zh-CN.mdx +12 -12
  6. package/package.json +1 -1
  7. package/src/database/repositories/dataImporter/__tests__/index.test.ts +11 -18
  8. package/src/database/repositories/dataImporter/index.ts +31 -46
  9. package/src/database/server/models/__tests__/_test_template.ts +1 -1
  10. package/src/database/server/models/__tests__/agent.test.ts +1 -1
  11. package/src/database/server/models/__tests__/asyncTask.test.ts +1 -1
  12. package/src/database/server/models/__tests__/chunk.test.ts +1 -1
  13. package/src/database/server/models/__tests__/file.test.ts +1 -1
  14. package/src/database/server/models/__tests__/knowledgeBase.test.ts +1 -2
  15. package/src/database/server/models/__tests__/message.test.ts +35 -72
  16. package/src/database/server/models/__tests__/nextauth.test.ts +1 -1
  17. package/src/database/server/models/__tests__/session.test.ts +1 -1
  18. package/src/database/server/models/__tests__/sessionGroup.test.ts +1 -2
  19. package/src/database/server/models/__tests__/topic.test.ts +1 -1
  20. package/src/database/server/models/__tests__/user.test.ts +1 -1
  21. package/src/database/server/models/_template.ts +2 -2
  22. package/src/database/server/models/agent.ts +17 -25
  23. package/src/database/server/models/asyncTask.ts +2 -2
  24. package/src/database/server/models/chunk.ts +14 -14
  25. package/src/database/server/models/embedding.ts +1 -1
  26. package/src/database/server/models/file.ts +8 -10
  27. package/src/database/server/models/knowledgeBase.ts +4 -6
  28. package/src/database/server/models/message.ts +54 -65
  29. package/src/database/server/models/plugin.ts +2 -2
  30. package/src/database/server/models/ragEval/dataset.ts +2 -2
  31. package/src/database/server/models/ragEval/datasetRecord.ts +3 -8
  32. package/src/database/server/models/ragEval/evaluation.ts +3 -2
  33. package/src/database/server/models/ragEval/evaluationRecord.ts +2 -2
  34. package/src/database/server/models/session.ts +38 -35
  35. package/src/database/server/models/sessionGroup.ts +4 -4
  36. package/src/database/server/models/thread.ts +2 -2
  37. package/src/database/server/models/topic.ts +48 -53
  38. package/src/database/server/models/user.ts +12 -12
  39. package/src/libs/agent-runtime/utils/streams/azureOpenai.test.ts +0 -1
  40. package/src/libs/next-auth/adapter/index.ts +1 -1
  41. package/src/server/routers/lambda/chunk.ts +2 -2
  42. package/vercel.json +1 -1
@@ -1,7 +1,7 @@
1
- import { and, eq } from 'drizzle-orm';
1
+ import { and, eq } from 'drizzle-orm/expressions';
2
2
 
3
- import { serverDB } from '@/database/server';
4
3
  import { NewEvaluationRecordsItem, evaluationRecords } from '@/database/schemas';
4
+ import { serverDB } from '@/database/server';
5
5
 
6
6
  export class EvaluationRecordModel {
7
7
  private userId: string;
@@ -31,7 +31,7 @@ export class SessionModel {
31
31
  }
32
32
  // **************** Query *************** //
33
33
 
34
- async query({ current = 0, pageSize = 9999 } = {}) {
34
+ query = async ({ current = 0, pageSize = 9999 } = {}) => {
35
35
  const offset = current * pageSize;
36
36
 
37
37
  return this.db.query.sessions.findMany({
@@ -41,9 +41,9 @@ export class SessionModel {
41
41
  where: and(eq(sessions.userId, this.userId), not(eq(sessions.slug, INBOX_SESSION_ID))),
42
42
  with: { agentsToSessions: { columns: {}, with: { agent: true } }, group: true },
43
43
  });
44
- }
44
+ };
45
45
 
46
- async queryWithGroups(): Promise<ChatSessionList> {
46
+ queryWithGroups = async (): Promise<ChatSessionList> => {
47
47
  // 查询所有会话
48
48
  const result = await this.query();
49
49
 
@@ -56,9 +56,9 @@ export class SessionModel {
56
56
  sessionGroups: groups as unknown as ChatSessionList['sessionGroups'],
57
57
  sessions: result.map((item) => this.mapSessionItem(item as any)),
58
58
  };
59
- }
59
+ };
60
60
 
61
- async queryByKeyword(keyword: string) {
61
+ queryByKeyword = async (keyword: string) => {
62
62
  if (!keyword) return [];
63
63
 
64
64
  const keywordLowerCase = keyword.toLowerCase();
@@ -66,11 +66,11 @@ export class SessionModel {
66
66
  const data = await this.findSessionsByKeywords({ keyword: keywordLowerCase });
67
67
 
68
68
  return data.map((item) => this.mapSessionItem(item as any));
69
- }
69
+ };
70
70
 
71
- async findByIdOrSlug(
71
+ findByIdOrSlug = async (
72
72
  idOrSlug: string,
73
- ): Promise<(SessionItem & { agent: AgentItem }) | undefined> {
73
+ ): Promise<(SessionItem & { agent: AgentItem }) | undefined> => {
74
74
  const result = await this.db.query.sessions.findFirst({
75
75
  where: and(
76
76
  or(eq(sessions.id, idOrSlug), eq(sessions.slug, idOrSlug)),
@@ -82,23 +82,22 @@ export class SessionModel {
82
82
  if (!result) return;
83
83
 
84
84
  return { ...result, agent: (result?.agentsToSessions?.[0] as any)?.agent } as any;
85
- }
85
+ };
86
86
 
87
- async count() {
87
+ count = async (): Promise<number> => {
88
88
  const result = await this.db
89
89
  .select({
90
- count: count(),
90
+ count: count(sessions.id),
91
91
  })
92
92
  .from(sessions)
93
- .where(eq(sessions.userId, this.userId))
94
- .execute();
93
+ .where(eq(sessions.userId, this.userId));
95
94
 
96
95
  return result[0].count;
97
- }
96
+ };
98
97
 
99
98
  // **************** Create *************** //
100
99
 
101
- async create({
100
+ create = async ({
102
101
  id = idGenerator('sessions'),
103
102
  type = 'agent',
104
103
  session = {},
@@ -110,7 +109,7 @@ export class SessionModel {
110
109
  session?: Partial<NewSession>;
111
110
  slug?: string;
112
111
  type: 'agent' | 'group';
113
- }): Promise<SessionItem> {
112
+ }): Promise<SessionItem> => {
114
113
  return this.db.transaction(async (trx) => {
115
114
  const newAgents = await trx
116
115
  .insert(agents)
@@ -143,9 +142,9 @@ export class SessionModel {
143
142
 
144
143
  return result[0];
145
144
  });
146
- }
145
+ };
147
146
 
148
- async createInbox() {
147
+ createInbox = async () => {
149
148
  const item = await this.db.query.sessions.findFirst({
150
149
  where: and(eq(sessions.userId, this.userId), eq(sessions.slug, INBOX_SESSION_ID)),
151
150
  });
@@ -158,9 +157,9 @@ export class SessionModel {
158
157
  slug: INBOX_SESSION_ID,
159
158
  type: 'agent',
160
159
  });
161
- }
160
+ };
162
161
 
163
- async batchCreate(newSessions: NewSession[]) {
162
+ batchCreate = async (newSessions: NewSession[]) => {
164
163
  const sessionsToInsert = newSessions.map((s) => {
165
164
  return {
166
165
  ...s,
@@ -170,9 +169,9 @@ export class SessionModel {
170
169
  });
171
170
 
172
171
  return this.db.insert(sessions).values(sessionsToInsert);
173
- }
172
+ };
174
173
 
175
- async duplicate(id: string, newTitle?: string) {
174
+ duplicate = async (id: string, newTitle?: string) => {
176
175
  const result = await this.findByIdOrSlug(id);
177
176
 
178
177
  if (!result) return;
@@ -193,49 +192,49 @@ export class SessionModel {
193
192
  },
194
193
  type: 'agent',
195
194
  });
196
- }
195
+ };
197
196
 
198
197
  // **************** Delete *************** //
199
198
 
200
199
  /**
201
200
  * Delete a session, also delete all messages and topics associated with it.
202
201
  */
203
- async delete(id: string) {
202
+ delete = async (id: string) => {
204
203
  return this.db
205
204
  .delete(sessions)
206
205
  .where(and(eq(sessions.id, id), eq(sessions.userId, this.userId)));
207
- }
206
+ };
208
207
 
209
208
  /**
210
209
  * Batch delete sessions, also delete all messages and topics associated with them.
211
210
  */
212
- async batchDelete(ids: string[]) {
211
+ batchDelete = async (ids: string[]) => {
213
212
  return this.db
214
213
  .delete(sessions)
215
214
  .where(and(inArray(sessions.id, ids), eq(sessions.userId, this.userId)));
216
- }
215
+ };
217
216
 
218
- async deleteAll() {
217
+ deleteAll = async () => {
219
218
  return this.db.delete(sessions).where(eq(sessions.userId, this.userId));
220
- }
219
+ };
221
220
  // **************** Update *************** //
222
221
 
223
- async update(id: string, data: Partial<SessionItem>) {
222
+ update = async (id: string, data: Partial<SessionItem>) => {
224
223
  return this.db
225
224
  .update(sessions)
226
225
  .set(data)
227
226
  .where(and(eq(sessions.id, id), eq(sessions.userId, this.userId)))
228
227
  .returning();
229
- }
228
+ };
230
229
 
231
- async updateConfig(id: string, data: Partial<AgentItem>) {
230
+ updateConfig = async (id: string, data: Partial<AgentItem>) => {
232
231
  if (Object.keys(data).length === 0) return;
233
232
 
234
233
  return this.db
235
234
  .update(agents)
236
235
  .set(data)
237
236
  .where(and(eq(agents.id, id), eq(agents.userId, this.userId)));
238
- }
237
+ };
239
238
 
240
239
  // **************** Helper *************** //
241
240
 
@@ -266,7 +265,11 @@ export class SessionModel {
266
265
  } as any;
267
266
  };
268
267
 
269
- async findSessionsByKeywords(params: { current?: number; keyword: string; pageSize?: number }) {
268
+ findSessionsByKeywords = async (params: {
269
+ current?: number;
270
+ keyword: string;
271
+ pageSize?: number;
272
+ }) => {
270
273
  const { keyword, pageSize = 9999, current = 0 } = params;
271
274
  const offset = current * pageSize;
272
275
  const results = await this.db.query.agents.findMany({
@@ -290,5 +293,5 @@ export class SessionModel {
290
293
  return results.map((item) => item.agentsToSessions[0].session);
291
294
  } catch {}
292
295
  return [];
293
- }
296
+ };
294
297
  }
@@ -46,14 +46,14 @@ export class SessionGroupModel {
46
46
  });
47
47
  };
48
48
 
49
- async update(id: string, value: Partial<SessionGroupItem>) {
49
+ update = async (id: string, value: Partial<SessionGroupItem>) => {
50
50
  return this.db
51
51
  .update(sessionGroups)
52
52
  .set({ ...value, updatedAt: new Date() })
53
53
  .where(and(eq(sessionGroups.id, id), eq(sessionGroups.userId, this.userId)));
54
- }
54
+ };
55
55
 
56
- async updateOrder(sortMap: { id: string; sort: number }[]) {
56
+ updateOrder = async (sortMap: { id: string; sort: number }[]) => {
57
57
  await this.db.transaction(async (tx) => {
58
58
  const updates = sortMap.map(({ id, sort }) => {
59
59
  return tx
@@ -64,7 +64,7 @@ export class SessionGroupModel {
64
64
 
65
65
  await Promise.all(updates);
66
66
  });
67
- }
67
+ };
68
68
 
69
69
  private genId = () => idGenerator('sessionGroups');
70
70
  }
@@ -71,10 +71,10 @@ export class ThreadModel {
71
71
  });
72
72
  };
73
73
 
74
- async update(id: string, value: Partial<ThreadItem>) {
74
+ update = async (id: string, value: Partial<ThreadItem>) => {
75
75
  return this.db
76
76
  .update(threads)
77
77
  .set({ ...value, updatedAt: new Date() })
78
78
  .where(and(eq(threads.id, id), eq(threads.userId, this.userId)));
79
- }
79
+ };
80
80
  }
@@ -29,46 +29,42 @@ export class TopicModel {
29
29
  }
30
30
  // **************** Query *************** //
31
31
 
32
- async query({ current = 0, pageSize = 9999, sessionId }: QueryTopicParams = {}) {
32
+ query = async ({ current = 0, pageSize = 9999, sessionId }: QueryTopicParams = {}) => {
33
33
  const offset = current * pageSize;
34
-
35
- return (
36
- this.db
37
- .select({
38
- createdAt: topics.createdAt,
39
- favorite: topics.favorite,
40
- historySummary: topics.historySummary,
41
- id: topics.id,
42
- metadata: topics.metadata,
43
- title: topics.title,
44
- updatedAt: topics.updatedAt,
45
- })
46
- .from(topics)
47
- .where(and(eq(topics.userId, this.userId), this.matchSession(sessionId)))
48
- // In boolean sorting, false is considered "smaller" than true.
49
- // So here we use desc to ensure that topics with favorite as true are in front.
50
- .orderBy(desc(topics.favorite), desc(topics.updatedAt))
51
- .limit(pageSize)
52
- .offset(offset)
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 queryAll(): Promise<TopicItem[]> {
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
- .execute();
69
- }
64
+ .where(eq(topics.userId, this.userId));
65
+ };
70
66
 
71
- async queryByKeyword(keyword: string, sessionId?: string | null): Promise<TopicItem[]> {
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 count() {
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 create(
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 batchCreate(topicParams: (CreateTopicParams & { id?: string })[]) {
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 duplicate(topicId: string, newTitle?: string) {
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 delete(id: string) {
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 batchDeleteBySessionId(sessionId?: string | null) {
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 batchDelete(ids: string[]) {
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 deleteAll() {
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 update(id: string, data: Partial<TopicItem>) {
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 getUserState() {
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 updateUser(value: Partial<UserItem>) {
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 deleteSetting() {
93
+ deleteSetting = async () => {
94
94
  return this.db.delete(userSettings).where(eq(userSettings.id, this.userId));
95
- }
95
+ };
96
96
 
97
- async updateSetting(value: Partial<UserSettings>) {
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 updatePreference(value: Partial<UserPreference>) {
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 updateGuide(value: Partial<UserGuide>) {
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
 
@@ -1,4 +1,3 @@
1
- import { desc } from 'drizzle-orm/expressions';
2
1
  import { describe, expect, it, vi } from 'vitest';
3
2
 
4
3
  import { AzureOpenAIStream } from './azureOpenai';
@@ -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';
package/vercel.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "installCommand": "bun install"
2
+ "installCommand": "npx bun@1.1.38 install"
3
3
  }