@lobehub/chat 1.15.6 → 1.15.8

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 (105) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +6 -6
  3. package/README.zh-CN.md +6 -6
  4. package/docs/self-hosting/server-database/docker-compose.mdx +2 -2
  5. package/docs/self-hosting/server-database/docker-compose.zh-CN.mdx +2 -2
  6. package/locales/ar/knowledgeBase.json +1 -0
  7. package/locales/ar/ragEval.json +91 -0
  8. package/locales/bg-BG/knowledgeBase.json +1 -0
  9. package/locales/bg-BG/ragEval.json +91 -0
  10. package/locales/de-DE/knowledgeBase.json +1 -0
  11. package/locales/de-DE/ragEval.json +91 -0
  12. package/locales/en-US/knowledgeBase.json +1 -0
  13. package/locales/en-US/ragEval.json +91 -0
  14. package/locales/es-ES/knowledgeBase.json +1 -0
  15. package/locales/es-ES/ragEval.json +91 -0
  16. package/locales/fr-FR/knowledgeBase.json +1 -0
  17. package/locales/fr-FR/ragEval.json +91 -0
  18. package/locales/it-IT/knowledgeBase.json +1 -0
  19. package/locales/it-IT/ragEval.json +91 -0
  20. package/locales/ja-JP/knowledgeBase.json +1 -0
  21. package/locales/ja-JP/ragEval.json +91 -0
  22. package/locales/ko-KR/knowledgeBase.json +1 -0
  23. package/locales/ko-KR/ragEval.json +91 -0
  24. package/locales/nl-NL/knowledgeBase.json +1 -0
  25. package/locales/nl-NL/ragEval.json +91 -0
  26. package/locales/pl-PL/knowledgeBase.json +1 -0
  27. package/locales/pl-PL/ragEval.json +91 -0
  28. package/locales/pt-BR/knowledgeBase.json +1 -0
  29. package/locales/pt-BR/ragEval.json +91 -0
  30. package/locales/ru-RU/knowledgeBase.json +1 -0
  31. package/locales/ru-RU/ragEval.json +91 -0
  32. package/locales/tr-TR/knowledgeBase.json +1 -0
  33. package/locales/tr-TR/ragEval.json +91 -0
  34. package/locales/vi-VN/knowledgeBase.json +1 -0
  35. package/locales/vi-VN/ragEval.json +91 -0
  36. package/locales/zh-CN/knowledgeBase.json +1 -0
  37. package/locales/zh-CN/ragEval.json +91 -0
  38. package/locales/zh-TW/knowledgeBase.json +1 -0
  39. package/locales/zh-TW/ragEval.json +91 -0
  40. package/package.json +2 -1
  41. package/src/app/(main)/repos/[id]/@menu/Head/index.tsx +4 -13
  42. package/src/app/(main)/repos/[id]/@menu/Menu/index.tsx +30 -21
  43. package/src/app/(main)/repos/[id]/@menu/default.tsx +8 -2
  44. package/src/app/(main)/repos/[id]/evals/components/Container.tsx +25 -0
  45. package/src/app/(main)/repos/[id]/evals/components/Tabs.tsx +35 -0
  46. package/src/app/(main)/repos/[id]/evals/dataset/CreateDataset/CreateForm.tsx +72 -0
  47. package/src/app/(main)/repos/[id]/evals/dataset/CreateDataset/index.tsx +37 -0
  48. package/src/app/(main)/repos/[id]/evals/dataset/DatasetDetail/index.tsx +126 -0
  49. package/src/app/(main)/repos/[id]/evals/dataset/DatasetList/Item.tsx +59 -0
  50. package/src/app/(main)/repos/[id]/evals/dataset/DatasetList/index.tsx +32 -0
  51. package/src/app/(main)/repos/[id]/evals/dataset/EmptyGuide/index.tsx +33 -0
  52. package/src/app/(main)/repos/[id]/evals/dataset/page.tsx +47 -0
  53. package/src/app/(main)/repos/[id]/evals/evaluation/CreateEvaluation/CreateForm.tsx +93 -0
  54. package/src/app/(main)/repos/[id]/evals/evaluation/CreateEvaluation/index.tsx +28 -0
  55. package/src/app/(main)/repos/[id]/evals/evaluation/CreateEvaluation/useModal.tsx +39 -0
  56. package/src/app/(main)/repos/[id]/evals/evaluation/EmptyGuide/index.tsx +25 -0
  57. package/src/app/(main)/repos/[id]/evals/evaluation/EvaluationList/index.tsx +209 -0
  58. package/src/app/(main)/repos/[id]/evals/evaluation/page.tsx +32 -0
  59. package/src/app/(main)/repos/[id]/evals/layout.tsx +22 -0
  60. package/src/app/(main)/repos/[id]/evals/page.tsx +9 -0
  61. package/src/app/(main)/repos/[id]/evals/type.ts +5 -0
  62. package/src/app/(main)/repos/[id]/not-found.tsx +3 -0
  63. package/src/app/(main)/settings/llm/components/ProviderConfig/index.tsx +2 -2
  64. package/src/components/FileIcon/index.tsx +2 -2
  65. package/src/config/featureFlags/schema.ts +3 -1
  66. package/src/database/server/migrations/0008_add_rag_evals.sql +120 -0
  67. package/src/database/server/migrations/meta/0008_snapshot.json +3463 -0
  68. package/src/database/server/migrations/meta/_journal.json +7 -0
  69. package/src/database/server/models/file.ts +11 -2
  70. package/src/database/server/models/ragEval/dataset.ts +59 -0
  71. package/src/database/server/models/ragEval/datasetRecord.ts +87 -0
  72. package/src/database/server/models/ragEval/evaluation.ts +96 -0
  73. package/src/database/server/models/ragEval/evaluationRecord.ts +64 -0
  74. package/src/database/server/models/ragEval/index.ts +4 -0
  75. package/src/database/server/schemas/lobechat/asyncTask.ts +24 -0
  76. package/src/database/server/schemas/lobechat/file.ts +2 -18
  77. package/src/database/server/schemas/lobechat/index.ts +2 -0
  78. package/src/database/server/schemas/lobechat/ragEvals.ts +105 -0
  79. package/src/database/server/schemas/lobechat/relations.ts +2 -1
  80. package/src/libs/agent-runtime/types/chat.ts +3 -0
  81. package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +3 -1
  82. package/src/libs/langchain/loaders/index.ts +1 -1
  83. package/src/locales/default/index.ts +2 -0
  84. package/src/locales/default/knowledgeBase.ts +1 -0
  85. package/src/locales/default/ragEval.ts +93 -0
  86. package/src/server/modules/S3/index.ts +11 -0
  87. package/src/server/routers/async/index.ts +2 -0
  88. package/src/server/routers/async/ragEval.ts +138 -0
  89. package/src/server/routers/lambda/index.ts +2 -1
  90. package/src/server/routers/lambda/ragEval.ts +296 -0
  91. package/src/services/ragEval.ts +67 -0
  92. package/src/services/upload.ts +11 -4
  93. package/src/store/file/slices/upload/action.ts +8 -6
  94. package/src/store/knowledgeBase/initialState.ts +3 -1
  95. package/src/store/knowledgeBase/slices/ragEval/actions/dataset.ts +88 -0
  96. package/src/store/knowledgeBase/slices/ragEval/actions/evaluation.ts +62 -0
  97. package/src/store/knowledgeBase/slices/ragEval/actions/index.ts +20 -0
  98. package/src/store/knowledgeBase/slices/ragEval/index.ts +2 -0
  99. package/src/store/knowledgeBase/slices/ragEval/initialState.ts +7 -0
  100. package/src/store/knowledgeBase/store.ts +9 -3
  101. package/src/store/serverConfig/selectors.test.ts +1 -0
  102. package/src/types/eval/dataset.ts +47 -0
  103. package/src/types/eval/evaluation.ts +53 -0
  104. package/src/types/eval/index.ts +3 -0
  105. package/src/types/eval/ragas.ts +9 -0
@@ -56,6 +56,13 @@
56
56
  "when": 1724254147447,
57
57
  "tag": "0007_fix_embedding_table",
58
58
  "breakpoints": true
59
+ },
60
+ {
61
+ "idx": 8,
62
+ "version": "7",
63
+ "when": 1725366565650,
64
+ "tag": "0008_add_rag_evals",
65
+ "breakpoints": true
59
66
  }
60
67
  ],
61
68
  "version": "6"
@@ -1,5 +1,5 @@
1
- import { asc, count, eq, ilike, inArray, notExists, sum } from 'drizzle-orm';
2
- import { and, desc } from 'drizzle-orm/expressions';
1
+ import { asc, count, eq, ilike, inArray, notExists, or, sum } from 'drizzle-orm';
2
+ import { and, desc, like } from 'drizzle-orm/expressions';
3
3
 
4
4
  import { serverDBEnv } from '@/config/db';
5
5
  import { serverDB } from '@/database/server/core/db';
@@ -280,4 +280,13 @@ export class FileModel {
280
280
  }
281
281
  }
282
282
  };
283
+
284
+ async findByNames(fileNames: string[]) {
285
+ return serverDB.query.files.findMany({
286
+ where: and(
287
+ or(...fileNames.map((name) => like(files.name, `${name}%`))),
288
+ eq(files.userId, this.userId),
289
+ ),
290
+ });
291
+ }
283
292
  }
@@ -0,0 +1,59 @@
1
+ import { and, desc, eq } from 'drizzle-orm';
2
+
3
+ import { serverDB } from '@/database/server';
4
+ import { NewEvalDatasetsItem, evalDatasets } from '@/database/server/schemas/lobechat';
5
+ import { RAGEvalDataSetItem } from '@/types/eval';
6
+
7
+ export class EvalDatasetModel {
8
+ private userId: string;
9
+
10
+ constructor(userId: string) {
11
+ this.userId = userId;
12
+ }
13
+
14
+ create = async (params: NewEvalDatasetsItem) => {
15
+ const [result] = await serverDB
16
+ .insert(evalDatasets)
17
+ .values({ ...params, userId: this.userId })
18
+ .returning();
19
+ return result;
20
+ };
21
+
22
+ delete = async (id: number) => {
23
+ return serverDB
24
+ .delete(evalDatasets)
25
+ .where(and(eq(evalDatasets.id, id), eq(evalDatasets.userId, this.userId)));
26
+ };
27
+
28
+ query = async (knowledgeBaseId: string): Promise<RAGEvalDataSetItem[]> => {
29
+ return serverDB
30
+ .select({
31
+ createdAt: evalDatasets.createdAt,
32
+ description: evalDatasets.description,
33
+ id: evalDatasets.id,
34
+ name: evalDatasets.name,
35
+ updatedAt: evalDatasets.updatedAt,
36
+ })
37
+ .from(evalDatasets)
38
+ .where(
39
+ and(
40
+ eq(evalDatasets.userId, this.userId),
41
+ eq(evalDatasets.knowledgeBaseId, knowledgeBaseId),
42
+ ),
43
+ )
44
+ .orderBy(desc(evalDatasets.createdAt));
45
+ };
46
+
47
+ findById = async (id: number) => {
48
+ return serverDB.query.evalDatasets.findFirst({
49
+ where: and(eq(evalDatasets.id, id), eq(evalDatasets.userId, this.userId)),
50
+ });
51
+ };
52
+
53
+ update = async (id: number, value: Partial<NewEvalDatasetsItem>) => {
54
+ return serverDB
55
+ .update(evalDatasets)
56
+ .set({ ...value, updatedAt: new Date() })
57
+ .where(and(eq(evalDatasets.id, id), eq(evalDatasets.userId, this.userId)));
58
+ };
59
+ }
@@ -0,0 +1,87 @@
1
+ import { and, eq, inArray } from 'drizzle-orm';
2
+
3
+ import { serverDB } from '@/database/server';
4
+ import {
5
+ NewEvalDatasetRecordsItem,
6
+ evalDatasetRecords,
7
+ files,
8
+ } from '@/database/server/schemas/lobechat';
9
+ import { EvalDatasetRecordRefFile } from '@/types/eval';
10
+
11
+ export class EvalDatasetRecordModel {
12
+ private userId: string;
13
+
14
+ constructor(userId: string) {
15
+ this.userId = userId;
16
+ }
17
+
18
+ create = async (params: NewEvalDatasetRecordsItem) => {
19
+ const [result] = await serverDB
20
+ .insert(evalDatasetRecords)
21
+ .values({ ...params, userId: this.userId })
22
+ .returning();
23
+ return result;
24
+ };
25
+
26
+ batchCreate = async (params: NewEvalDatasetRecordsItem[]) => {
27
+ const [result] = await serverDB
28
+ .insert(evalDatasetRecords)
29
+ .values(params.map((item) => ({ ...item, userId: this.userId })))
30
+ .returning();
31
+
32
+ return result;
33
+ };
34
+
35
+ delete = async (id: number) => {
36
+ return serverDB
37
+ .delete(evalDatasetRecords)
38
+ .where(and(eq(evalDatasetRecords.id, id), eq(evalDatasetRecords.userId, this.userId)));
39
+ };
40
+
41
+ query = async (datasetId: number) => {
42
+ const list = await serverDB.query.evalDatasetRecords.findMany({
43
+ where: and(
44
+ eq(evalDatasetRecords.datasetId, datasetId),
45
+ eq(evalDatasetRecords.userId, this.userId),
46
+ ),
47
+ });
48
+ const fileList = list.flatMap((item) => item.referenceFiles).filter(Boolean) as string[];
49
+
50
+ const fileItems = await serverDB
51
+ .select({ fileType: files.fileType, id: files.id, name: files.name })
52
+ .from(files)
53
+ .where(and(inArray(files.id, fileList), eq(files.userId, this.userId)))
54
+ .execute();
55
+
56
+ return list.map((item) => {
57
+ return {
58
+ ...item,
59
+ referenceFiles: (item.referenceFiles?.map((fileId) => {
60
+ return fileItems.find((file) => file.id === fileId);
61
+ }) || []) as EvalDatasetRecordRefFile[],
62
+ };
63
+ });
64
+ };
65
+
66
+ findByDatasetId = async (datasetId: number) => {
67
+ return serverDB.query.evalDatasetRecords.findMany({
68
+ where: and(
69
+ eq(evalDatasetRecords.datasetId, datasetId),
70
+ eq(evalDatasetRecords.userId, this.userId),
71
+ ),
72
+ });
73
+ };
74
+
75
+ findById = async (id: number) => {
76
+ return serverDB.query.evalDatasetRecords.findFirst({
77
+ where: and(eq(evalDatasetRecords.id, id), eq(evalDatasetRecords.userId, this.userId)),
78
+ });
79
+ };
80
+
81
+ update = async (id: number, value: Partial<NewEvalDatasetRecordsItem>) => {
82
+ return serverDB
83
+ .update(evalDatasetRecords)
84
+ .set(value)
85
+ .where(and(eq(evalDatasetRecords.id, id), eq(evalDatasetRecords.userId, this.userId)));
86
+ };
87
+ }
@@ -0,0 +1,96 @@
1
+ import { SQL, and, count, desc, eq, inArray } from 'drizzle-orm';
2
+
3
+ import { serverDB } from '@/database/server';
4
+ import {
5
+ NewEvalEvaluationItem,
6
+ evalDatasets,
7
+ evalEvaluation,
8
+ evaluationRecords,
9
+ } from '@/database/server/schemas/lobechat';
10
+ import { EvalEvaluationStatus, RAGEvalEvaluationItem } from '@/types/eval';
11
+
12
+ export class EvalEvaluationModel {
13
+ private userId: string;
14
+
15
+ constructor(userId: string) {
16
+ this.userId = userId;
17
+ }
18
+
19
+ create = async (params: NewEvalEvaluationItem) => {
20
+ const [result] = await serverDB
21
+ .insert(evalEvaluation)
22
+ .values({ ...params, userId: this.userId })
23
+ .returning();
24
+ return result;
25
+ };
26
+
27
+ delete = async (id: number) => {
28
+ return serverDB
29
+ .delete(evalEvaluation)
30
+ .where(and(eq(evalEvaluation.id, id), eq(evalEvaluation.userId, this.userId)));
31
+ };
32
+
33
+ queryByKnowledgeBaseId = async (knowledgeBaseId: string) => {
34
+ const evaluations = await serverDB
35
+ .select({
36
+ createdAt: evalEvaluation.createdAt,
37
+ dataset: {
38
+ id: evalDatasets.id,
39
+ name: evalDatasets.name,
40
+ },
41
+ evalRecordsUrl: evalEvaluation.evalRecordsUrl,
42
+ id: evalEvaluation.id,
43
+ name: evalEvaluation.name,
44
+ status: evalEvaluation.status,
45
+ updatedAt: evalEvaluation.updatedAt,
46
+ })
47
+ .from(evalEvaluation)
48
+ .leftJoin(evalDatasets, eq(evalDatasets.id, evalEvaluation.datasetId))
49
+ .orderBy(desc(evalEvaluation.createdAt))
50
+ .where(
51
+ and(
52
+ eq(evalEvaluation.userId, this.userId),
53
+ eq(evalEvaluation.knowledgeBaseId, knowledgeBaseId),
54
+ ),
55
+ );
56
+
57
+ // 然后查询每个评估的记录统计
58
+ const evaluationIds = evaluations.map((evals) => evals.id);
59
+
60
+ const recordStats = await serverDB
61
+ .select({
62
+ evaluationId: evaluationRecords.evaluationId,
63
+ success: count(evaluationRecords.status).if(
64
+ eq(evaluationRecords.status, EvalEvaluationStatus.Success),
65
+ ) as SQL<number>,
66
+ total: count(),
67
+ })
68
+ .from(evaluationRecords)
69
+ .where(inArray(evaluationRecords.evaluationId, evaluationIds))
70
+ .groupBy(evaluationRecords.evaluationId);
71
+
72
+ return evaluations.map((evaluation) => {
73
+ const stats = recordStats.find((stat) => stat.evaluationId === evaluation.id);
74
+
75
+ return {
76
+ ...evaluation,
77
+ recordsStats: stats
78
+ ? { success: Number(stats.success), total: Number(stats.total) }
79
+ : { success: 0, total: 0 },
80
+ } as RAGEvalEvaluationItem;
81
+ });
82
+ };
83
+
84
+ findById = async (id: number) => {
85
+ return serverDB.query.evalEvaluation.findFirst({
86
+ where: and(eq(evalEvaluation.id, id), eq(evalEvaluation.userId, this.userId)),
87
+ });
88
+ };
89
+
90
+ update = async (id: number, value: Partial<NewEvalEvaluationItem>) => {
91
+ return serverDB
92
+ .update(evalEvaluation)
93
+ .set(value)
94
+ .where(and(eq(evalEvaluation.id, id), eq(evalEvaluation.userId, this.userId)));
95
+ };
96
+ }
@@ -0,0 +1,64 @@
1
+ import { and, eq } from 'drizzle-orm';
2
+
3
+ import { serverDB } from '@/database/server';
4
+ import { NewEvaluationRecordsItem, evaluationRecords } from '@/database/server/schemas/lobechat';
5
+
6
+ export class EvaluationRecordModel {
7
+ private userId: string;
8
+
9
+ constructor(userId: string) {
10
+ this.userId = userId;
11
+ }
12
+
13
+ create = async (params: NewEvaluationRecordsItem) => {
14
+ const [result] = await serverDB
15
+ .insert(evaluationRecords)
16
+ .values({ ...params, userId: this.userId })
17
+ .returning();
18
+ return result;
19
+ };
20
+
21
+ batchCreate = async (params: NewEvaluationRecordsItem[]) => {
22
+ return serverDB
23
+ .insert(evaluationRecords)
24
+ .values(params.map((item) => ({ ...item, userId: this.userId })))
25
+ .returning();
26
+ };
27
+
28
+ delete = async (id: number) => {
29
+ return serverDB
30
+ .delete(evaluationRecords)
31
+ .where(and(eq(evaluationRecords.id, id), eq(evaluationRecords.userId, this.userId)));
32
+ };
33
+
34
+ query = async (reportId: number) => {
35
+ return serverDB.query.evaluationRecords.findMany({
36
+ where: and(
37
+ eq(evaluationRecords.evaluationId, reportId),
38
+ eq(evaluationRecords.userId, this.userId),
39
+ ),
40
+ });
41
+ };
42
+
43
+ findById = async (id: number) => {
44
+ return serverDB.query.evaluationRecords.findFirst({
45
+ where: and(eq(evaluationRecords.id, id), eq(evaluationRecords.userId, this.userId)),
46
+ });
47
+ };
48
+
49
+ findByEvaluationId = async (evaluationId: number) => {
50
+ return serverDB.query.evaluationRecords.findMany({
51
+ where: and(
52
+ eq(evaluationRecords.evaluationId, evaluationId),
53
+ eq(evaluationRecords.userId, this.userId),
54
+ ),
55
+ });
56
+ };
57
+
58
+ update = async (id: number, value: Partial<NewEvaluationRecordsItem>) => {
59
+ return serverDB
60
+ .update(evaluationRecords)
61
+ .set(value)
62
+ .where(and(eq(evaluationRecords.id, id), eq(evaluationRecords.userId, this.userId)));
63
+ };
64
+ }
@@ -0,0 +1,4 @@
1
+ export * from './dataset';
2
+ export * from './datasetRecord';
3
+ export * from './evaluation';
4
+ export * from './evaluationRecord';
@@ -0,0 +1,24 @@
1
+ /* eslint-disable sort-keys-fix/sort-keys-fix */
2
+ import { integer, jsonb, pgTable, text, uuid } from 'drizzle-orm/pg-core';
3
+
4
+ import { createdAt, updatedAt } from './_helpers';
5
+ import { users } from './user';
6
+
7
+ export const asyncTasks = pgTable('async_tasks', {
8
+ id: uuid('id').defaultRandom().primaryKey(),
9
+ type: text('type'),
10
+
11
+ status: text('status'),
12
+ error: jsonb('error'),
13
+
14
+ userId: text('user_id')
15
+ .references(() => users.id, { onDelete: 'cascade' })
16
+ .notNull(),
17
+ duration: integer('duration'),
18
+
19
+ createdAt: createdAt(),
20
+ updatedAt: updatedAt(),
21
+ });
22
+
23
+ export type NewAsyncTaskItem = typeof asyncTasks.$inferInsert;
24
+ export type AsyncTaskSelectItem = typeof asyncTasks.$inferSelect;
@@ -11,28 +11,12 @@ import {
11
11
  } from 'drizzle-orm/pg-core';
12
12
  import { createInsertSchema } from 'drizzle-zod';
13
13
 
14
- import { chunks } from '@/database/server/schemas/lobechat/rag';
15
-
16
14
  import { idGenerator } from '../../utils/idGenerator';
17
15
  import { createdAt, updatedAt } from './_helpers';
16
+ import { asyncTasks } from './asyncTask';
17
+ import { chunks } from './rag';
18
18
  import { users } from './user';
19
19
 
20
- export const asyncTasks = pgTable('async_tasks', {
21
- id: uuid('id').defaultRandom().primaryKey(),
22
- type: text('type'),
23
- status: text('status'),
24
- error: jsonb('error'),
25
- userId: text('user_id')
26
- .references(() => users.id, { onDelete: 'cascade' })
27
- .notNull(),
28
- duration: integer('duration'),
29
- createdAt: createdAt(),
30
- updatedAt: updatedAt(),
31
- });
32
-
33
- export type NewAsyncTaskItem = typeof asyncTasks.$inferInsert;
34
- export type AsyncTaskSelectItem = typeof asyncTasks.$inferSelect;
35
-
36
20
  export const globalFiles = pgTable('global_files', {
37
21
  hashId: varchar('hash_id', { length: 64 }).primaryKey(),
38
22
  fileType: varchar('file_type', { length: 255 }).notNull(),
@@ -1,9 +1,11 @@
1
1
  export * from './agent';
2
+ export * from './asyncTask';
2
3
  export * from './discover';
3
4
  export * from './file';
4
5
  export * from './message';
5
6
  export * from './nextauth';
6
7
  export * from './rag';
8
+ export * from './ragEvals';
7
9
  export * from './relations';
8
10
  export * from './session';
9
11
  export * from './topic';
@@ -0,0 +1,105 @@
1
+ /* eslint-disable sort-keys-fix/sort-keys-fix */
2
+ import { integer, jsonb, pgTable, text, uuid } from 'drizzle-orm/pg-core';
3
+
4
+ import { DEFAULT_EMBEDDING_MODEL, DEFAULT_MODEL } from '@/const/settings';
5
+ import { EvalEvaluationStatus } from '@/types/eval';
6
+
7
+ import { createdAt, updatedAt } from './_helpers';
8
+ import { knowledgeBases } from './file';
9
+ import { embeddings } from './rag';
10
+ import { users } from './user';
11
+
12
+ export const evalDatasets = pgTable('rag_eval_datasets', {
13
+ id: integer('id').generatedAlwaysAsIdentity({ startWith: 30_000 }).primaryKey(),
14
+
15
+ description: text('description'),
16
+ name: text('name').notNull(),
17
+
18
+ knowledgeBaseId: text('knowledge_base_id').references(() => knowledgeBases.id, {
19
+ onDelete: 'cascade',
20
+ }),
21
+ userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
22
+
23
+ updatedAt: updatedAt(),
24
+ createdAt: createdAt(),
25
+ });
26
+
27
+ export type NewEvalDatasetsItem = typeof evalDatasets.$inferInsert;
28
+ export type EvalDatasetsSelectItem = typeof evalDatasets.$inferSelect;
29
+
30
+ export const evalDatasetRecords = pgTable('rag_eval_dataset_records', {
31
+ id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
32
+ datasetId: integer('dataset_id')
33
+ .references(() => evalDatasets.id, { onDelete: 'cascade' })
34
+ .notNull(),
35
+
36
+ ideal: text('ideal'),
37
+ question: text('question'),
38
+ referenceFiles: text('reference_files').array(),
39
+ metadata: jsonb('metadata'),
40
+
41
+ userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
42
+ createdAt: createdAt(),
43
+ });
44
+
45
+ export type NewEvalDatasetRecordsItem = typeof evalDatasetRecords.$inferInsert;
46
+ export type EvalDatasetRecordsSelectItem = typeof evalDatasetRecords.$inferSelect;
47
+
48
+ export const evalEvaluation = pgTable('rag_eval_evaluations', {
49
+ id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
50
+ name: text('name').notNull(),
51
+ description: text('description'),
52
+
53
+ evalRecordsUrl: text('eval_records_url'),
54
+ status: text('status').$defaultFn(() => EvalEvaluationStatus.Pending),
55
+ error: jsonb('error'),
56
+
57
+ datasetId: integer('dataset_id')
58
+ .references(() => evalDatasets.id, { onDelete: 'cascade' })
59
+ .notNull(),
60
+ knowledgeBaseId: text('knowledge_base_id').references(() => knowledgeBases.id, {
61
+ onDelete: 'cascade',
62
+ }),
63
+ languageModel: text('language_model').$defaultFn(() => DEFAULT_MODEL),
64
+ embeddingModel: text('embedding_model').$defaultFn(() => DEFAULT_EMBEDDING_MODEL),
65
+
66
+ userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
67
+ createdAt: createdAt(),
68
+ updatedAt: updatedAt(),
69
+ });
70
+
71
+ export type NewEvalEvaluationItem = typeof evalEvaluation.$inferInsert;
72
+ export type EvalEvaluationSelectItem = typeof evalEvaluation.$inferSelect;
73
+
74
+ export const evaluationRecords = pgTable('rag_eval_evaluation_records', {
75
+ id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
76
+
77
+ question: text('question').notNull(),
78
+ answer: text('answer'),
79
+ context: text('context').array(),
80
+ ideal: text('ideal'),
81
+
82
+ status: text('status').$defaultFn(() => EvalEvaluationStatus.Pending),
83
+ error: jsonb('error'),
84
+
85
+ languageModel: text('language_model'),
86
+ embeddingModel: text('embedding_model'),
87
+
88
+ questionEmbeddingId: uuid('question_embedding_id').references(() => embeddings.id, {
89
+ onDelete: 'set null',
90
+ }),
91
+
92
+ duration: integer('duration'),
93
+ datasetRecordId: integer('dataset_record_id')
94
+ .references(() => evalDatasetRecords.id, { onDelete: 'cascade' })
95
+ .notNull(),
96
+ evaluationId: integer('evaluation_id')
97
+ .references(() => evalEvaluation.id, { onDelete: 'cascade' })
98
+ .notNull(),
99
+
100
+ userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
101
+ createdAt: createdAt(),
102
+ });
103
+
104
+ export type NewEvaluationRecordsItem = typeof evaluationRecords.$inferInsert;
105
+ export type EvaluationRecordsSelectItem = typeof evaluationRecords.$inferSelect;
@@ -3,8 +3,9 @@ import { relations } from 'drizzle-orm';
3
3
  import { pgTable, primaryKey, text } from 'drizzle-orm/pg-core';
4
4
 
5
5
  import { agents, agentsFiles, agentsKnowledgeBases } from './agent';
6
+ import { asyncTasks } from './asyncTask';
6
7
  import { agentsTags, plugins, pluginsTags, tags } from './discover';
7
- import { asyncTasks, files, knowledgeBases } from './file';
8
+ import { files, knowledgeBases } from './file';
8
9
  import { messages, messagesFiles } from './message';
9
10
  import { unstructuredChunks } from './rag';
10
11
  import { sessionGroups, sessions } from './session';
@@ -72,6 +72,8 @@ export interface ChatStreamPayload {
72
72
  * @default openai
73
73
  */
74
74
  provider?: string;
75
+
76
+ responseMode?: 'streamText' | 'json';
75
77
  /**
76
78
  * @title 是否开启流式请求
77
79
  * @default true
@@ -84,6 +86,7 @@ export interface ChatStreamPayload {
84
86
  temperature: number;
85
87
  tool_choice?: string;
86
88
  tools?: ChatCompletionTool[];
89
+
87
90
  /**
88
91
  * @title 控制生成文本中最高概率的单个令牌
89
92
  * @default 1
@@ -149,7 +149,7 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
149
149
  this.baseURL = this.client.baseURL;
150
150
  }
151
151
 
152
- async chat(payload: ChatStreamPayload, options?: ChatCompetitionOptions) {
152
+ async chat({ responseMode, ...payload }: ChatStreamPayload, options?: ChatCompetitionOptions) {
153
153
  try {
154
154
  const postPayload = chatCompletion?.handlePayload
155
155
  ? chatCompletion.handlePayload(payload, this._options)
@@ -186,6 +186,8 @@ export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>
186
186
  debugResponse(response);
187
187
  }
188
188
 
189
+ if (responseMode === 'json') return Response.json(response);
190
+
189
191
  const stream = transformResponseToStream(response as unknown as OpenAI.ChatCompletion);
190
192
 
191
193
  return StreamingResponse(OpenAIStream(stream, options?.callback), {
@@ -27,7 +27,7 @@ export class ChunkingLoader {
27
27
  const fileBlob = new Blob([Buffer.from(content)]);
28
28
  const txt = this.uint8ArrayToString(content);
29
29
 
30
- const type = this.getType(filename);
30
+ const type = this.getType(filename?.toLowerCase());
31
31
 
32
32
  switch (type) {
33
33
  case 'code': {
@@ -13,6 +13,7 @@ import migration from './migration';
13
13
  import modelProvider from './modelProvider';
14
14
  import plugin from './plugin';
15
15
  import portal from './portal';
16
+ import ragEval from './ragEval';
16
17
  import setting from './setting';
17
18
  import welcome from './welcome';
18
19
 
@@ -31,6 +32,7 @@ const resources = {
31
32
  modelProvider,
32
33
  plugin,
33
34
  portal,
35
+ ragEval,
34
36
  setting,
35
37
  tool,
36
38
  welcome,
@@ -23,6 +23,7 @@ export default {
23
23
  title: '新建知识库',
24
24
  },
25
25
  tab: {
26
+ evals: '评测',
26
27
  files: '文档',
27
28
  settings: '设置',
28
29
  testing: '召回测试',