@lobehub/chat 1.36.13 → 1.36.14
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 +25 -0
- package/changelog/v1.json +9 -0
- package/docs/self-hosting/environment-variables/s3.mdx +15 -0
- package/docs/self-hosting/environment-variables/s3.zh-CN.mdx +13 -0
- package/package.json +1 -1
- package/src/config/db.ts +4 -4
- package/src/database/server/models/__tests__/file.test.ts +3 -21
- package/src/database/server/models/_template.ts +1 -2
- package/src/database/server/models/agent.ts +2 -4
- package/src/database/server/models/asyncTask.ts +1 -2
- package/src/database/server/models/chunk.ts +2 -2
- package/src/database/server/models/embedding.ts +2 -2
- package/src/database/server/models/file.ts +4 -5
- package/src/database/server/models/knowledgeBase.ts +1 -2
- package/src/database/server/models/message.ts +1 -1
- package/src/database/server/models/session.ts +2 -2
- package/src/database/server/models/sessionGroup.ts +1 -2
- package/src/database/server/models/thread.ts +1 -2
- package/src/database/server/models/topic.ts +2 -2
- package/src/database/server/models/user.ts +1 -1
- package/src/server/routers/async/file.ts +3 -2
- package/src/server/routers/lambda/file.ts +6 -2
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.36.14](https://github.com/lobehub/lobe-chat/compare/v1.36.13...v1.36.14)
|
6
|
+
|
7
|
+
<sup>Released on **2024-12-12**</sup>
|
8
|
+
|
9
|
+
#### ♻ Code Refactoring
|
10
|
+
|
11
|
+
- **misc**: Refactor database file model to remove server env.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### Code refactoring
|
19
|
+
|
20
|
+
- **misc**: Refactor database file model to remove server env, closes [#4990](https://github.com/lobehub/lobe-chat/issues/4990) ([284f790](https://github.com/lobehub/lobe-chat/commit/284f790))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.36.13](https://github.com/lobehub/lobe-chat/compare/v1.36.12...v1.36.13)
|
6
31
|
|
7
32
|
<sup>Released on **2024-12-11**</sup>
|
package/changelog/v1.json
CHANGED
@@ -81,3 +81,18 @@ Assuming the S3 service provider's domain is s3.example.net, the bucket is mybuc
|
|
81
81
|
- virtual-host: `mybucket.s3.example.net/config.env`
|
82
82
|
|
83
83
|
</Callout>
|
84
|
+
|
85
|
+
### `LLM_VISION_IMAGE_USE_BASE64`
|
86
|
+
|
87
|
+
- Type: Optional
|
88
|
+
- Description: Set to `1` to use base64 encoding for image upload.
|
89
|
+
- Default: undefined
|
90
|
+
- Example: `1`
|
91
|
+
|
92
|
+
When set to `1`, LobeChat will convert images to base64 encoding before
|
93
|
+
uploading them to the LLM model. When encountering the following error,
|
94
|
+
please consider configuring this environment variable to `1`:
|
95
|
+
|
96
|
+
```log
|
97
|
+
Route: [xai] ProviderBizError: Fetching images over plain http:// is not supported.
|
98
|
+
```
|
@@ -80,3 +80,16 @@ LobeChat 支持多模态的 AI 会话,包括将图片、文件等非结构化
|
|
80
80
|
- virtual-host : `mybucket.s3.example.net/config.env`
|
81
81
|
|
82
82
|
</Callout>
|
83
|
+
|
84
|
+
### `LLM_VISION_IMAGE_USE_BASE64`
|
85
|
+
|
86
|
+
- 类型:可选
|
87
|
+
- 描述:设置为 1 则使用 base64 编码上传图片
|
88
|
+
- 默认值:undefined
|
89
|
+
- 示例:`1`
|
90
|
+
|
91
|
+
当设置为 `1` 时,LobeChat 会将图片转换为 base64 编码后上传到 LLM 模型中,当遇到如下错误时请考虑配置该环境变量为 1
|
92
|
+
|
93
|
+
```log
|
94
|
+
Route: [xai] ProviderBizError: Fetching images over plain http:// is not supported.
|
95
|
+
```
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.36.
|
3
|
+
"version": "1.36.14",
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
5
5
|
"keywords": [
|
6
6
|
"framework",
|
package/src/config/db.ts
CHANGED
@@ -11,20 +11,20 @@ export const getServerDBConfig = () => {
|
|
11
11
|
DATABASE_TEST_URL: process.env.DATABASE_TEST_URL,
|
12
12
|
DATABASE_URL: process.env.DATABASE_URL,
|
13
13
|
|
14
|
-
DISABLE_REMOVE_GLOBAL_FILE: process.env.DISABLE_REMOVE_GLOBAL_FILE === '1',
|
15
|
-
|
16
14
|
KEY_VAULTS_SECRET: process.env.KEY_VAULTS_SECRET,
|
17
15
|
|
18
16
|
NEXT_PUBLIC_ENABLED_SERVER_SERVICE: process.env.NEXT_PUBLIC_SERVICE_MODE === 'server',
|
17
|
+
|
18
|
+
REMOVE_GLOBAL_FILE: process.env.DISABLE_REMOVE_GLOBAL_FILE !== '0',
|
19
19
|
},
|
20
20
|
server: {
|
21
21
|
DATABASE_DRIVER: z.enum(['neon', 'node']),
|
22
22
|
DATABASE_TEST_URL: z.string().optional(),
|
23
23
|
DATABASE_URL: z.string().optional(),
|
24
24
|
|
25
|
-
DISABLE_REMOVE_GLOBAL_FILE: z.boolean().optional(),
|
26
|
-
|
27
25
|
KEY_VAULTS_SECRET: z.string().optional(),
|
26
|
+
|
27
|
+
REMOVE_GLOBAL_FILE: z.boolean().optional(),
|
28
28
|
},
|
29
29
|
});
|
30
30
|
};
|
@@ -8,21 +8,7 @@ import { FilesTabs, SortType } from '@/types/files';
|
|
8
8
|
import { files, globalFiles, knowledgeBaseFiles, knowledgeBases, users } from '../../../schemas';
|
9
9
|
import { FileModel } from '../file';
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
let DISABLE_REMOVE_GLOBAL_FILE = false;
|
14
|
-
|
15
|
-
vi.mock('@/config/db', async () => ({
|
16
|
-
get serverDBEnv() {
|
17
|
-
return {
|
18
|
-
get DISABLE_REMOVE_GLOBAL_FILE() {
|
19
|
-
return DISABLE_REMOVE_GLOBAL_FILE;
|
20
|
-
},
|
21
|
-
DATABASE_TEST_URL: process.env.DATABASE_TEST_URL,
|
22
|
-
DATABASE_DRIVER: 'node',
|
23
|
-
};
|
24
|
-
},
|
25
|
-
}));
|
11
|
+
const serverDB = await getTestDBInstance();
|
26
12
|
|
27
13
|
const userId = 'file-model-test-user-id';
|
28
14
|
const fileModel = new FileModel(serverDB, userId);
|
@@ -146,7 +132,6 @@ describe('FileModel', () => {
|
|
146
132
|
expect(globalFile).toBeUndefined();
|
147
133
|
});
|
148
134
|
it('should delete a file by id but global file not removed ', async () => {
|
149
|
-
DISABLE_REMOVE_GLOBAL_FILE = true;
|
150
135
|
await fileModel.createGlobalFile({
|
151
136
|
hashId: '1',
|
152
137
|
url: 'https://example.com/file1.txt',
|
@@ -162,7 +147,7 @@ describe('FileModel', () => {
|
|
162
147
|
fileHash: '1',
|
163
148
|
});
|
164
149
|
|
165
|
-
await fileModel.delete(id);
|
150
|
+
await fileModel.delete(id, false);
|
166
151
|
|
167
152
|
const file = await serverDB.query.files.findFirst({ where: eq(files.id, id) });
|
168
153
|
const globalFile = await serverDB.query.globalFiles.findFirst({
|
@@ -171,7 +156,6 @@ describe('FileModel', () => {
|
|
171
156
|
|
172
157
|
expect(file).toBeUndefined();
|
173
158
|
expect(globalFile).toBeDefined();
|
174
|
-
DISABLE_REMOVE_GLOBAL_FILE = false;
|
175
159
|
});
|
176
160
|
});
|
177
161
|
|
@@ -225,7 +209,6 @@ describe('FileModel', () => {
|
|
225
209
|
expect(globalFilesResult2).toHaveLength(0);
|
226
210
|
});
|
227
211
|
it('should delete multiple files but not remove global files if DISABLE_REMOVE_GLOBAL_FILE=true', async () => {
|
228
|
-
DISABLE_REMOVE_GLOBAL_FILE = true;
|
229
212
|
await fileModel.createGlobalFile({
|
230
213
|
hashId: '1',
|
231
214
|
url: 'https://example.com/file1.txt',
|
@@ -260,7 +243,7 @@ describe('FileModel', () => {
|
|
260
243
|
|
261
244
|
expect(globalFilesResult).toHaveLength(2);
|
262
245
|
|
263
|
-
await fileModel.deleteMany([file1.id, file2.id]);
|
246
|
+
await fileModel.deleteMany([file1.id, file2.id], false);
|
264
247
|
|
265
248
|
const remainingFiles = await serverDB.query.files.findMany({
|
266
249
|
where: eq(files.userId, userId),
|
@@ -271,7 +254,6 @@ describe('FileModel', () => {
|
|
271
254
|
|
272
255
|
expect(remainingFiles).toHaveLength(0);
|
273
256
|
expect(globalFilesResult2).toHaveLength(2);
|
274
|
-
DISABLE_REMOVE_GLOBAL_FILE = false;
|
275
257
|
});
|
276
258
|
});
|
277
259
|
|
@@ -1,7 +1,4 @@
|
|
1
|
-
import { inArray } from 'drizzle-orm';
|
2
|
-
import { and, desc, eq } from 'drizzle-orm/expressions';
|
3
|
-
|
4
|
-
import { LobeChatDatabase } from '@/database/type';
|
1
|
+
import { and, desc, eq, inArray } from 'drizzle-orm/expressions';
|
5
2
|
|
6
3
|
import {
|
7
4
|
agents,
|
@@ -11,6 +8,7 @@ import {
|
|
11
8
|
files,
|
12
9
|
knowledgeBases,
|
13
10
|
} from '@/database/schemas';
|
11
|
+
import { LobeChatDatabase } from '@/database/type';
|
14
12
|
|
15
13
|
export class AgentModel {
|
16
14
|
private userId: string;
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import {
|
2
|
-
import { and, desc, isNull } from 'drizzle-orm/expressions';
|
1
|
+
import { cosineDistance, count, sql } from 'drizzle-orm';
|
2
|
+
import { and, asc, desc, eq, inArray, isNull } from 'drizzle-orm/expressions';
|
3
3
|
import { chunk } from 'lodash-es';
|
4
4
|
|
5
5
|
import { LobeChatDatabase } from '@/database/type';
|
@@ -2,7 +2,6 @@ import { asc, count, eq, ilike, inArray, notExists, or, sum } from 'drizzle-orm'
|
|
2
2
|
import { and, desc, like } from 'drizzle-orm/expressions';
|
3
3
|
import type { PgTransaction } from 'drizzle-orm/pg-core';
|
4
4
|
|
5
|
-
import { serverDBEnv } from '@/config/db';
|
6
5
|
import { LobeChatDatabase } from '@/database/type';
|
7
6
|
import { FilesTabs, QueryFileListParams, SortType } from '@/types/files';
|
8
7
|
|
@@ -67,7 +66,7 @@ export class FileModel {
|
|
67
66
|
};
|
68
67
|
};
|
69
68
|
|
70
|
-
delete = async (id: string) => {
|
69
|
+
delete = async (id: string, removeGlobalFile: boolean = true) => {
|
71
70
|
const file = await this.findById(id);
|
72
71
|
if (!file) return;
|
73
72
|
|
@@ -89,7 +88,7 @@ export class FileModel {
|
|
89
88
|
|
90
89
|
// delete the file from global file if it is not used by other files
|
91
90
|
// if `DISABLE_REMOVE_GLOBAL_FILE` is true, we will not remove the global file
|
92
|
-
if (fileCount === 0 &&
|
91
|
+
if (fileCount === 0 && removeGlobalFile) {
|
93
92
|
await trx.delete(globalFiles).where(eq(globalFiles.hashId, fileHash));
|
94
93
|
|
95
94
|
return file;
|
@@ -112,7 +111,7 @@ export class FileModel {
|
|
112
111
|
return parseInt(result[0].totalSize!) || 0;
|
113
112
|
};
|
114
113
|
|
115
|
-
deleteMany = async (ids: string[]) => {
|
114
|
+
deleteMany = async (ids: string[], removeGlobalFile: boolean = true) => {
|
116
115
|
const fileList = await this.findByIds(ids);
|
117
116
|
const hashList = fileList.map((file) => file.fileHash!);
|
118
117
|
|
@@ -144,7 +143,7 @@ export class FileModel {
|
|
144
143
|
|
145
144
|
const needToDeleteList = fileHashCounts.filter((item) => item.count === 0);
|
146
145
|
|
147
|
-
if (needToDeleteList.length === 0 ||
|
146
|
+
if (needToDeleteList.length === 0 || !removeGlobalFile) return;
|
148
147
|
|
149
148
|
// delete the file from global file if it is not used by other files
|
150
149
|
await trx.delete(globalFiles).where(
|
@@ -1,5 +1,4 @@
|
|
1
|
-
import { eq, inArray } from 'drizzle-orm';
|
2
|
-
import { and, desc } from 'drizzle-orm/expressions';
|
1
|
+
import { and, desc, eq, inArray } from 'drizzle-orm/expressions';
|
3
2
|
|
4
3
|
import { LobeChatDatabase } from '@/database/type';
|
5
4
|
import { KnowledgeBaseItem } from '@/types/knowledgeBase';
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import { count } from 'drizzle-orm';
|
2
2
|
import { and, asc, desc, eq, gte, inArray, isNull, like, lt } from 'drizzle-orm/expressions';
|
3
3
|
|
4
|
-
import { idGenerator } from '@/database/utils/idGenerator';
|
5
4
|
import { LobeChatDatabase } from '@/database/type';
|
5
|
+
import { idGenerator } from '@/database/utils/idGenerator';
|
6
6
|
import { getFullFileUrl } from '@/server/utils/files';
|
7
7
|
import {
|
8
8
|
ChatFileItem,
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import { Column,
|
2
|
-
import { and, desc, eq, not, or } from 'drizzle-orm/expressions';
|
1
|
+
import { Column, count, sql } from 'drizzle-orm';
|
2
|
+
import { and, asc, desc, eq, inArray, like, not, or } from 'drizzle-orm/expressions';
|
3
3
|
|
4
4
|
import { appEnv } from '@/config/app';
|
5
5
|
import { INBOX_SESSION_ID } from '@/const/session';
|
@@ -1,5 +1,4 @@
|
|
1
|
-
import { eq } from 'drizzle-orm';
|
2
|
-
import { and, asc, desc } from 'drizzle-orm/expressions';
|
1
|
+
import { and, asc, desc, eq } from 'drizzle-orm/expressions';
|
3
2
|
|
4
3
|
import { LobeChatDatabase } from '@/database/type';
|
5
4
|
import { idGenerator } from '@/database/utils/idGenerator';
|
@@ -1,5 +1,4 @@
|
|
1
|
-
import { eq } from 'drizzle-orm';
|
2
|
-
import { and, desc } from 'drizzle-orm/expressions';
|
1
|
+
import { and, desc, eq } from 'drizzle-orm/expressions';
|
3
2
|
|
4
3
|
import { LobeChatDatabase } from '@/database/type';
|
5
4
|
import { CreateThreadParams, ThreadStatus } from '@/types/topic';
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import { Column, count,
|
2
|
-
import { and, desc, eq, exists, isNull, like, or } from 'drizzle-orm/expressions';
|
1
|
+
import { Column, count, sql } from 'drizzle-orm';
|
2
|
+
import { and, desc, eq, exists, inArray, isNull, like, or } from 'drizzle-orm/expressions';
|
3
3
|
|
4
4
|
import { LobeChatDatabase } from '@/database/type';
|
5
5
|
import { idGenerator } from '@/database/utils/idGenerator';
|
@@ -3,14 +3,15 @@ import { chunk } from 'lodash-es';
|
|
3
3
|
import pMap from 'p-map';
|
4
4
|
import { z } from 'zod';
|
5
5
|
|
6
|
+
import { serverDBEnv } from '@/config/db';
|
6
7
|
import { fileEnv } from '@/config/file';
|
7
8
|
import { DEFAULT_EMBEDDING_MODEL } from '@/const/settings';
|
9
|
+
import { NewChunkItem, NewEmbeddingsItem } from '@/database/schemas';
|
8
10
|
import { serverDB } from '@/database/server';
|
9
11
|
import { ASYNC_TASK_TIMEOUT, AsyncTaskModel } from '@/database/server/models/asyncTask';
|
10
12
|
import { ChunkModel } from '@/database/server/models/chunk';
|
11
13
|
import { EmbeddingModel } from '@/database/server/models/embedding';
|
12
14
|
import { FileModel } from '@/database/server/models/file';
|
13
|
-
import { NewChunkItem, NewEmbeddingsItem } from '@/database/schemas';
|
14
15
|
import { ModelProvider } from '@/libs/agent-runtime';
|
15
16
|
import { asyncAuthedProcedure, asyncRouter as router } from '@/libs/trpc/async';
|
16
17
|
import { initAgentRuntimeWithUserPayload } from '@/server/modules/AgentRuntime';
|
@@ -175,7 +176,7 @@ export const fileRouter = router({
|
|
175
176
|
console.error(e);
|
176
177
|
// if file not found, delete it from db
|
177
178
|
if ((e as any).Code === 'NoSuchKey') {
|
178
|
-
await ctx.fileModel.delete(input.fileId);
|
179
|
+
await ctx.fileModel.delete(input.fileId, serverDBEnv.REMOVE_GLOBAL_FILE);
|
179
180
|
throw new TRPCError({ code: 'BAD_REQUEST', message: 'File not found' });
|
180
181
|
}
|
181
182
|
}
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { TRPCError } from '@trpc/server';
|
2
2
|
import { z } from 'zod';
|
3
3
|
|
4
|
+
import { serverDBEnv } from '@/config/db';
|
4
5
|
import { serverDB } from '@/database/server';
|
5
6
|
import { AsyncTaskModel } from '@/database/server/models/asyncTask';
|
6
7
|
import { ChunkModel } from '@/database/server/models/chunk';
|
@@ -153,7 +154,7 @@ export const fileRouter = router({
|
|
153
154
|
}),
|
154
155
|
|
155
156
|
removeFile: fileProcedure.input(z.object({ id: z.string() })).mutation(async ({ input, ctx }) => {
|
156
|
-
const file = await ctx.fileModel.delete(input.id);
|
157
|
+
const file = await ctx.fileModel.delete(input.id, serverDBEnv.REMOVE_GLOBAL_FILE);
|
157
158
|
|
158
159
|
if (!file) return;
|
159
160
|
|
@@ -184,7 +185,10 @@ export const fileRouter = router({
|
|
184
185
|
removeFiles: fileProcedure
|
185
186
|
.input(z.object({ ids: z.array(z.string()) }))
|
186
187
|
.mutation(async ({ input, ctx }) => {
|
187
|
-
const needToRemoveFileList = await ctx.fileModel.deleteMany(
|
188
|
+
const needToRemoveFileList = await ctx.fileModel.deleteMany(
|
189
|
+
input.ids,
|
190
|
+
serverDBEnv.REMOVE_GLOBAL_FILE,
|
191
|
+
);
|
188
192
|
|
189
193
|
if (!needToRemoveFileList || needToRemoveFileList.length === 0) return;
|
190
194
|
|