@lobehub/chat 1.112.5 → 1.113.1

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 (40) hide show
  1. package/.vscode/settings.json +1 -1
  2. package/CHANGELOG.md +50 -0
  3. package/changelog/v1.json +10 -0
  4. package/package.json +3 -3
  5. package/packages/const/src/image.ts +9 -1
  6. package/packages/model-runtime/src/bfl/createImage.test.ts +846 -0
  7. package/packages/model-runtime/src/bfl/createImage.ts +279 -0
  8. package/packages/model-runtime/src/bfl/index.test.ts +269 -0
  9. package/packages/model-runtime/src/bfl/index.ts +49 -0
  10. package/packages/model-runtime/src/bfl/types.ts +113 -0
  11. package/packages/model-runtime/src/index.ts +1 -0
  12. package/packages/model-runtime/src/qwen/createImage.ts +37 -82
  13. package/packages/model-runtime/src/runtimeMap.ts +2 -0
  14. package/packages/model-runtime/src/utils/asyncifyPolling.test.ts +491 -0
  15. package/packages/model-runtime/src/utils/asyncifyPolling.ts +175 -0
  16. package/src/app/(backend)/api/webhooks/casdoor/route.ts +2 -1
  17. package/src/app/(backend)/api/webhooks/clerk/route.ts +2 -1
  18. package/src/app/(backend)/api/webhooks/logto/route.ts +2 -1
  19. package/src/app/(backend)/webapi/user/avatar/[id]/[image]/route.ts +2 -1
  20. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/index.tsx +1 -1
  21. package/src/config/aiModels/bfl.ts +145 -0
  22. package/src/config/aiModels/index.ts +3 -0
  23. package/src/config/llm.ts +6 -1
  24. package/src/config/modelProviders/bfl.ts +21 -0
  25. package/src/config/modelProviders/index.ts +3 -0
  26. package/src/database/server/models/ragEval/dataset.ts +9 -7
  27. package/src/database/server/models/ragEval/datasetRecord.ts +12 -10
  28. package/src/database/server/models/ragEval/evaluation.ts +10 -8
  29. package/src/database/server/models/ragEval/evaluationRecord.ts +11 -9
  30. package/src/server/routers/async/file.ts +1 -1
  31. package/src/server/routers/async/ragEval.ts +4 -4
  32. package/src/server/routers/lambda/chunk.ts +1 -1
  33. package/src/server/routers/lambda/ragEval.ts +4 -4
  34. package/src/server/routers/lambda/user.ts +1 -1
  35. package/src/server/services/chunk/index.ts +2 -2
  36. package/src/server/services/nextAuthUser/index.test.ts +1 -1
  37. package/src/server/services/nextAuthUser/index.ts +6 -4
  38. package/src/server/services/user/index.test.ts +3 -1
  39. package/src/server/services/user/index.ts +14 -8
  40. package/src/store/image/slices/generationConfig/hooks.ts +6 -10
@@ -6,18 +6,20 @@ import {
6
6
  evalEvaluation,
7
7
  evaluationRecords,
8
8
  } from '@/database/schemas';
9
- import { serverDB } from '@/database/server';
9
+ import { LobeChatDatabase } from '@/database/type';
10
10
  import { EvalEvaluationStatus, RAGEvalEvaluationItem } from '@/types/eval';
11
11
 
12
12
  export class EvalEvaluationModel {
13
13
  private userId: string;
14
+ private db: LobeChatDatabase;
14
15
 
15
- constructor(userId: string) {
16
+ constructor(db: LobeChatDatabase, userId: string) {
17
+ this.db = db;
16
18
  this.userId = userId;
17
19
  }
18
20
 
19
21
  create = async (params: NewEvalEvaluationItem) => {
20
- const [result] = await serverDB
22
+ const [result] = await this.db
21
23
  .insert(evalEvaluation)
22
24
  .values({ ...params, userId: this.userId })
23
25
  .returning();
@@ -25,13 +27,13 @@ export class EvalEvaluationModel {
25
27
  };
26
28
 
27
29
  delete = async (id: number) => {
28
- return serverDB
30
+ return this.db
29
31
  .delete(evalEvaluation)
30
32
  .where(and(eq(evalEvaluation.id, id), eq(evalEvaluation.userId, this.userId)));
31
33
  };
32
34
 
33
35
  queryByKnowledgeBaseId = async (knowledgeBaseId: string) => {
34
- const evaluations = await serverDB
36
+ const evaluations = await this.db
35
37
  .select({
36
38
  createdAt: evalEvaluation.createdAt,
37
39
  dataset: {
@@ -57,7 +59,7 @@ export class EvalEvaluationModel {
57
59
  // 然后查询每个评估的记录统计
58
60
  const evaluationIds = evaluations.map((evals) => evals.id);
59
61
 
60
- const recordStats = await serverDB
62
+ const recordStats = await this.db
61
63
  .select({
62
64
  evaluationId: evaluationRecords.evaluationId,
63
65
  success: count(evaluationRecords.status).if(
@@ -82,13 +84,13 @@ export class EvalEvaluationModel {
82
84
  };
83
85
 
84
86
  findById = async (id: number) => {
85
- return serverDB.query.evalEvaluation.findFirst({
87
+ return this.db.query.evalEvaluation.findFirst({
86
88
  where: and(eq(evalEvaluation.id, id), eq(evalEvaluation.userId, this.userId)),
87
89
  });
88
90
  };
89
91
 
90
92
  update = async (id: number, value: Partial<NewEvalEvaluationItem>) => {
91
- return serverDB
93
+ return this.db
92
94
  .update(evalEvaluation)
93
95
  .set(value)
94
96
  .where(and(eq(evalEvaluation.id, id), eq(evalEvaluation.userId, this.userId)));
@@ -1,17 +1,19 @@
1
1
  import { and, eq } from 'drizzle-orm';
2
2
 
3
3
  import { NewEvaluationRecordsItem, evaluationRecords } from '@/database/schemas';
4
- import { serverDB } from '@/database/server';
4
+ import { LobeChatDatabase } from '@/database/type';
5
5
 
6
6
  export class EvaluationRecordModel {
7
7
  private userId: string;
8
+ private db: LobeChatDatabase;
8
9
 
9
- constructor(userId: string) {
10
+ constructor(db: LobeChatDatabase, userId: string) {
11
+ this.db = db;
10
12
  this.userId = userId;
11
13
  }
12
14
 
13
15
  create = async (params: NewEvaluationRecordsItem) => {
14
- const [result] = await serverDB
16
+ const [result] = await this.db
15
17
  .insert(evaluationRecords)
16
18
  .values({ ...params, userId: this.userId })
17
19
  .returning();
@@ -19,20 +21,20 @@ export class EvaluationRecordModel {
19
21
  };
20
22
 
21
23
  batchCreate = async (params: NewEvaluationRecordsItem[]) => {
22
- return serverDB
24
+ return this.db
23
25
  .insert(evaluationRecords)
24
26
  .values(params.map((item) => ({ ...item, userId: this.userId })))
25
27
  .returning();
26
28
  };
27
29
 
28
30
  delete = async (id: number) => {
29
- return serverDB
31
+ return this.db
30
32
  .delete(evaluationRecords)
31
33
  .where(and(eq(evaluationRecords.id, id), eq(evaluationRecords.userId, this.userId)));
32
34
  };
33
35
 
34
36
  query = async (reportId: number) => {
35
- return serverDB.query.evaluationRecords.findMany({
37
+ return this.db.query.evaluationRecords.findMany({
36
38
  where: and(
37
39
  eq(evaluationRecords.evaluationId, reportId),
38
40
  eq(evaluationRecords.userId, this.userId),
@@ -41,13 +43,13 @@ export class EvaluationRecordModel {
41
43
  };
42
44
 
43
45
  findById = async (id: number) => {
44
- return serverDB.query.evaluationRecords.findFirst({
46
+ return this.db.query.evaluationRecords.findFirst({
45
47
  where: and(eq(evaluationRecords.id, id), eq(evaluationRecords.userId, this.userId)),
46
48
  });
47
49
  };
48
50
 
49
51
  findByEvaluationId = async (evaluationId: number) => {
50
- return serverDB.query.evaluationRecords.findMany({
52
+ return this.db.query.evaluationRecords.findMany({
51
53
  where: and(
52
54
  eq(evaluationRecords.evaluationId, evaluationId),
53
55
  eq(evaluationRecords.userId, this.userId),
@@ -56,7 +58,7 @@ export class EvaluationRecordModel {
56
58
  };
57
59
 
58
60
  update = async (id: number, value: Partial<NewEvaluationRecordsItem>) => {
59
- return serverDB
61
+ return this.db
60
62
  .update(evaluationRecords)
61
63
  .set(value)
62
64
  .where(and(eq(evaluationRecords.id, id), eq(evaluationRecords.userId, this.userId)));
@@ -32,7 +32,7 @@ const fileProcedure = asyncAuthedProcedure.use(async (opts) => {
32
32
  ctx: {
33
33
  asyncTaskModel: new AsyncTaskModel(ctx.serverDB, ctx.userId),
34
34
  chunkModel: new ChunkModel(ctx.serverDB, ctx.userId),
35
- chunkService: new ChunkService(ctx.userId),
35
+ chunkService: new ChunkService(ctx.serverDB, ctx.userId),
36
36
  embeddingModel: new EmbeddingModel(ctx.serverDB, ctx.userId),
37
37
  fileModel: new FileModel(ctx.serverDB, ctx.userId),
38
38
  fileService: new FileService(ctx.serverDB, ctx.userId),
@@ -25,11 +25,11 @@ const ragEvalProcedure = asyncAuthedProcedure.use(async (opts) => {
25
25
  return opts.next({
26
26
  ctx: {
27
27
  chunkModel: new ChunkModel(ctx.serverDB, ctx.userId),
28
- chunkService: new ChunkService(ctx.userId),
29
- datasetRecordModel: new EvalDatasetRecordModel(ctx.userId),
28
+ chunkService: new ChunkService(ctx.serverDB, ctx.userId),
29
+ datasetRecordModel: new EvalDatasetRecordModel(ctx.serverDB, ctx.userId),
30
30
  embeddingModel: new EmbeddingModel(ctx.serverDB, ctx.userId),
31
- evalRecordModel: new EvaluationRecordModel(ctx.userId),
32
- evaluationModel: new EvalEvaluationModel(ctx.userId),
31
+ evalRecordModel: new EvaluationRecordModel(ctx.serverDB, ctx.userId),
32
+ evaluationModel: new EvalEvaluationModel(ctx.serverDB, ctx.userId),
33
33
  fileModel: new FileModel(ctx.serverDB, ctx.userId),
34
34
  },
35
35
  });
@@ -26,7 +26,7 @@ const chunkProcedure = authedProcedure
26
26
  ctx: {
27
27
  asyncTaskModel: new AsyncTaskModel(ctx.serverDB, ctx.userId),
28
28
  chunkModel: new ChunkModel(ctx.serverDB, ctx.userId),
29
- chunkService: new ChunkService(ctx.userId),
29
+ chunkService: new ChunkService(ctx.serverDB, ctx.userId),
30
30
  embeddingModel: new EmbeddingModel(ctx.serverDB, ctx.userId),
31
31
  fileModel: new FileModel(ctx.serverDB, ctx.userId),
32
32
  messageModel: new MessageModel(ctx.serverDB, ctx.userId),
@@ -35,11 +35,11 @@ const ragEvalProcedure = authedProcedure
35
35
 
36
36
  return opts.next({
37
37
  ctx: {
38
- datasetModel: new EvalDatasetModel(ctx.userId),
38
+ datasetModel: new EvalDatasetModel(ctx.serverDB, ctx.userId),
39
39
  fileModel: new FileModel(ctx.serverDB, ctx.userId),
40
- datasetRecordModel: new EvalDatasetRecordModel(ctx.userId),
41
- evaluationModel: new EvalEvaluationModel(ctx.userId),
42
- evaluationRecordModel: new EvaluationRecordModel(ctx.userId),
40
+ datasetRecordModel: new EvalDatasetRecordModel(ctx.serverDB, ctx.userId),
41
+ evaluationModel: new EvalEvaluationModel(ctx.serverDB, ctx.userId),
42
+ evaluationRecordModel: new EvaluationRecordModel(ctx.serverDB, ctx.userId),
43
43
  fileService: new FileService(ctx.serverDB, ctx.userId),
44
44
  },
45
45
  });
@@ -58,7 +58,7 @@ export const userRouter = router({
58
58
  if (enableClerk) {
59
59
  const user = await ctx.clerkAuth.getCurrentUser();
60
60
  if (user) {
61
- const userService = new UserService();
61
+ const userService = new UserService(ctx.serverDB);
62
62
 
63
63
  await userService.createUser(user.id, {
64
64
  created_at: user.createdAt,
@@ -1,7 +1,7 @@
1
1
  import { ClientSecretPayload } from '@/const/auth';
2
2
  import { AsyncTaskModel } from '@/database/models/asyncTask';
3
3
  import { FileModel } from '@/database/models/file';
4
- import { serverDB } from '@/database/server';
4
+ import { LobeChatDatabase } from '@/database/type';
5
5
  import { ChunkContentParams, ContentChunk } from '@/server/modules/ContentChunk';
6
6
  import { createAsyncCaller } from '@/server/routers/async';
7
7
  import {
@@ -17,7 +17,7 @@ export class ChunkService {
17
17
  private fileModel: FileModel;
18
18
  private asyncTaskModel: AsyncTaskModel;
19
19
 
20
- constructor(userId: string) {
20
+ constructor(serverDB: LobeChatDatabase, userId: string) {
21
21
  this.userId = userId;
22
22
 
23
23
  this.chunkClient = new ContentChunk();
@@ -24,7 +24,7 @@ describe('NextAuthUserService', () => {
24
24
 
25
25
  beforeEach(async () => {
26
26
  vi.clearAllMocks();
27
- service = new NextAuthUserService();
27
+ service = new NextAuthUserService(serverDB);
28
28
  });
29
29
 
30
30
  describe('safeUpdateUser', () => {
@@ -2,15 +2,17 @@ import { NextResponse } from 'next/server';
2
2
 
3
3
  import { UserModel } from '@/database/models/user';
4
4
  import { UserItem } from '@/database/schemas';
5
- import { serverDB } from '@/database/server';
5
+ import { LobeChatDatabase } from '@/database/type';
6
6
  import { pino } from '@/libs/logger';
7
7
  import { LobeNextAuthDbAdapter } from '@/libs/next-auth/adapter';
8
8
 
9
9
  export class NextAuthUserService {
10
10
  adapter;
11
+ private db: LobeChatDatabase;
11
12
 
12
- constructor() {
13
- this.adapter = LobeNextAuthDbAdapter(serverDB);
13
+ constructor(db: LobeChatDatabase) {
14
+ this.db = db;
15
+ this.adapter = LobeNextAuthDbAdapter(db);
14
16
  }
15
17
 
16
18
  safeUpdateUser = async (
@@ -27,7 +29,7 @@ export class NextAuthUserService {
27
29
 
28
30
  // 2. If found, Update user data from provider
29
31
  if (user?.id) {
30
- const userModel = new UserModel(serverDB, user.id);
32
+ const userModel = new UserModel(this.db, user.id);
31
33
 
32
34
  // Perform update
33
35
  await userModel.updateUser({
@@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
3
 
4
4
  import { UserModel } from '@/database/models/user';
5
5
  import { UserItem } from '@/database/schemas';
6
+ import { LobeChatDatabase } from '@/database/type';
6
7
  import { pino } from '@/libs/logger';
7
8
  import { AgentService } from '@/server/services/agent';
8
9
 
@@ -46,6 +47,7 @@ vi.mock('@/server/services/agent', () => ({
46
47
 
47
48
  let service: UserService;
48
49
  const mockUserId = 'test-user-id';
50
+ const mockDB = {} as LobeChatDatabase;
49
51
 
50
52
  // Mock user data
51
53
  const mockUserJSON: UserJSON = {
@@ -62,7 +64,7 @@ const mockUserJSON: UserJSON = {
62
64
  } as unknown as UserJSON;
63
65
 
64
66
  beforeEach(() => {
65
- service = new UserService();
67
+ service = new UserService(mockDB);
66
68
  vi.clearAllMocks();
67
69
  });
68
70
 
@@ -1,7 +1,7 @@
1
1
  import { UserJSON } from '@clerk/backend';
2
2
 
3
3
  import { UserModel } from '@/database/models/user';
4
- import { serverDB } from '@/database/server';
4
+ import { LobeChatDatabase } from '@/database/type';
5
5
  import { initializeServerAnalytics } from '@/libs/analytics';
6
6
  import { pino } from '@/libs/logger';
7
7
  import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
@@ -9,9 +9,15 @@ import { S3 } from '@/server/modules/S3';
9
9
  import { AgentService } from '@/server/services/agent';
10
10
 
11
11
  export class UserService {
12
+ private db: LobeChatDatabase;
13
+
14
+ constructor(db: LobeChatDatabase) {
15
+ this.db = db;
16
+ }
17
+
12
18
  createUser = async (id: string, params: UserJSON) => {
13
19
  // Check if user already exists
14
- const res = await UserModel.findById(serverDB, id);
20
+ const res = await UserModel.findById(this.db, id);
15
21
 
16
22
  // If user already exists, skip creating a new user
17
23
  if (res)
@@ -33,7 +39,7 @@ export class UserService {
33
39
  /* ↑ cloud slot ↑ */
34
40
 
35
41
  // 2. create user in database
36
- await UserModel.createUser(serverDB, {
42
+ await UserModel.createUser(this.db, {
37
43
  avatar: params.image_url,
38
44
  clerkCreatedAt: new Date(params.created_at),
39
45
  email: email?.email_address,
@@ -45,7 +51,7 @@ export class UserService {
45
51
  });
46
52
 
47
53
  // 3. Create an inbox session for the user
48
- const agentService = new AgentService(serverDB, id);
54
+ const agentService = new AgentService(this.db, id);
49
55
  await agentService.createInbox();
50
56
 
51
57
  /* ↓ cloud slot ↓ */
@@ -73,14 +79,14 @@ export class UserService {
73
79
  };
74
80
 
75
81
  deleteUser = async (id: string) => {
76
- await UserModel.deleteUser(serverDB, id);
82
+ await UserModel.deleteUser(this.db, id);
77
83
  };
78
84
 
79
85
  updateUser = async (id: string, params: UserJSON) => {
80
- const userModel = new UserModel(serverDB, id);
86
+ const userModel = new UserModel(this.db, id);
81
87
 
82
88
  // Check if user already exists
83
- const res = await UserModel.findById(serverDB, id);
89
+ const res = await UserModel.findById(this.db, id);
84
90
 
85
91
  // If user not exists, skip update the user
86
92
  if (!res)
@@ -111,7 +117,7 @@ export class UserService {
111
117
  };
112
118
 
113
119
  getUserApiKeys = async (id: string) => {
114
- return UserModel.getUserApiKeys(serverDB, id, KeyVaultsGateKeeper.getUserKeyVaults);
120
+ return UserModel.getUserApiKeys(this.db, id, KeyVaultsGateKeeper.getUserKeyVaults);
115
121
  };
116
122
 
117
123
  getUserAvatar = async (id: string, image: string) => {
@@ -77,17 +77,13 @@ export function useDimensionControl() {
77
77
  const aspectRatioOptions = useMemo(() => {
78
78
  const modelOptions = paramsSchema?.aspectRatio?.enum || [];
79
79
 
80
- // 合并选项,优先使用预设选项,然后添加模型特有的选项
81
- const allOptions = [...PRESET_ASPECT_RATIOS];
80
+ // 如果 schema 里面有 aspectRatio 并且不为空,直接使用 schema 里面的选项
81
+ if (modelOptions.length > 0) {
82
+ return modelOptions;
83
+ }
82
84
 
83
- // 添加模型选项中不在预设中的选项
84
- modelOptions.forEach((option) => {
85
- if (!allOptions.includes(option)) {
86
- allOptions.push(option);
87
- }
88
- });
89
-
90
- return allOptions;
85
+ // 否则使用预设选项
86
+ return PRESET_ASPECT_RATIOS;
91
87
  }, [paramsSchema]);
92
88
 
93
89
  // 只要不是所有维度相关的控件都不显示,那么这个容器就应该显示