@lobehub/chat 1.84.25 → 1.84.26
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/package.json +3 -3
- package/src/config/aiModels/ollama.ts +12 -2
- package/src/libs/langchain/loaders/epub/index.ts +4 -2
- package/src/libs/mcp/__tests__/__snapshots__/index.test.ts.snap +3 -1
- package/src/server/routers/async/file.ts +1 -1
- package/src/server/routers/lambda/file.ts +1 -1
- package/src/server/routers/lambda/importer.ts +4 -2
- package/src/server/routers/lambda/message.ts +2 -2
- package/src/server/routers/lambda/ragEval.ts +1 -1
- package/src/server/routers/lambda/user.ts +2 -2
- package/src/server/services/file/index.ts +46 -0
- package/src/server/utils/tempFileManager.ts +5 -8
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.84.26](https://github.com/lobehub/lobe-chat/compare/v1.84.25...v1.84.26)
|
6
|
+
|
7
|
+
<sup>Released on **2025-05-08**</sup>
|
8
|
+
|
9
|
+
#### 💄 Styles
|
10
|
+
|
11
|
+
- **misc**: Add qwen3 for ollama.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### Styles
|
19
|
+
|
20
|
+
- **misc**: Add qwen3 for ollama, closes [#7746](https://github.com/lobehub/lobe-chat/issues/7746) ([806d905](https://github.com/lobehub/lobe-chat/commit/806d905))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.84.25](https://github.com/lobehub/lobe-chat/compare/v1.84.24...v1.84.25)
|
6
31
|
|
7
32
|
<sup>Released on **2025-05-08**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.84.
|
3
|
+
"version": "1.84.26",
|
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",
|
@@ -121,7 +121,7 @@
|
|
121
121
|
"dependencies": {
|
122
122
|
"@ant-design/icons": "^5.6.1",
|
123
123
|
"@ant-design/pro-components": "^2.8.7",
|
124
|
-
"@anthropic-ai/sdk": "^0.
|
124
|
+
"@anthropic-ai/sdk": "^0.40.1",
|
125
125
|
"@auth/core": "^0.38.0",
|
126
126
|
"@aws-sdk/client-bedrock-runtime": "^3.779.0",
|
127
127
|
"@aws-sdk/client-s3": "^3.779.0",
|
@@ -199,7 +199,7 @@
|
|
199
199
|
"langfuse": "^3.37.1",
|
200
200
|
"langfuse-core": "^3.37.1",
|
201
201
|
"lodash-es": "^4.17.21",
|
202
|
-
"lucide-react": "^0.
|
202
|
+
"lucide-react": "^0.508.0",
|
203
203
|
"mammoth": "^1.9.0",
|
204
204
|
"mdast-util-to-markdown": "^2.1.2",
|
205
205
|
"modern-screenshot": "^4.6.0",
|
@@ -89,11 +89,22 @@ const ollamaChatModels: AIChatModelCard[] = [
|
|
89
89
|
description:
|
90
90
|
'QwQ 是 Qwen 系列的推理模型。与传统的指令调优模型相比,QwQ 具备思考和推理的能力,能够在下游任务中,尤其是困难问题上,显著提升性能。QwQ-32B 是中型推理模型,能够在与最先进的推理模型(如 DeepSeek-R1、o1-mini)竞争时取得可观的表现。',
|
91
91
|
displayName: 'QwQ 32B',
|
92
|
-
enabled: true,
|
93
92
|
id: 'qwq',
|
94
93
|
releasedAt: '2024-11-28',
|
95
94
|
type: 'chat',
|
96
95
|
},
|
96
|
+
{
|
97
|
+
abilities: {
|
98
|
+
functionCall: true,
|
99
|
+
},
|
100
|
+
contextWindowTokens: 65_536,
|
101
|
+
description: 'Qwen3 是阿里巴巴的新一代大规模语言模型,以优异的性能支持多元化的应用需求。',
|
102
|
+
displayName: 'Qwen3 7B',
|
103
|
+
enabled: true,
|
104
|
+
id: 'qwen3',
|
105
|
+
type: 'chat',
|
106
|
+
},
|
107
|
+
|
97
108
|
{
|
98
109
|
contextWindowTokens: 128_000,
|
99
110
|
description: 'Qwen2.5 是阿里巴巴的新一代大规模语言模型,以优异的性能支持多元化的应用需求。',
|
@@ -115,7 +126,6 @@ const ollamaChatModels: AIChatModelCard[] = [
|
|
115
126
|
contextWindowTokens: 128_000,
|
116
127
|
description: 'Qwen2.5 是阿里巴巴的新一代大规模语言模型,以优异的性能支持多元化的应用需求。',
|
117
128
|
displayName: 'Qwen2.5 7B',
|
118
|
-
enabled: true,
|
119
129
|
id: 'qwen2.5',
|
120
130
|
type: 'chat',
|
121
131
|
},
|
@@ -2,13 +2,15 @@ import { EPubLoader as Loader } from '@langchain/community/document_loaders/fs/e
|
|
2
2
|
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
|
3
3
|
|
4
4
|
import { TempFileManager } from '@/server/utils/tempFileManager';
|
5
|
+
import { nanoid } from '@/utils/uuid';
|
5
6
|
|
6
7
|
import { loaderConfig } from '../config';
|
7
8
|
|
8
9
|
export const EPubLoader = async (content: Uint8Array) => {
|
9
|
-
const tempManager = new TempFileManager();
|
10
|
+
const tempManager = new TempFileManager('epub-');
|
11
|
+
|
10
12
|
try {
|
11
|
-
const tempPath = await tempManager.writeTempFile(content);
|
13
|
+
const tempPath = await tempManager.writeTempFile(content, `${nanoid()}.epub`);
|
12
14
|
const loader = new Loader(tempPath);
|
13
15
|
const documents = await loader.load();
|
14
16
|
|
@@ -21,9 +21,11 @@ exports[`MCPClient > Stdio Transport > should list tools via stdio 1`] = `
|
|
21
21
|
"name": "echo",
|
22
22
|
},
|
23
23
|
{
|
24
|
-
"annotations": {},
|
25
24
|
"description": "Lists all available tools and methods",
|
26
25
|
"inputSchema": {
|
26
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
27
|
+
"additionalProperties": false,
|
28
|
+
"properties": {},
|
27
29
|
"type": "object",
|
28
30
|
},
|
29
31
|
"name": "debug",
|
@@ -35,7 +35,7 @@ const fileProcedure = asyncAuthedProcedure.use(async (opts) => {
|
|
35
35
|
chunkService: new ChunkService(ctx.userId),
|
36
36
|
embeddingModel: new EmbeddingModel(ctx.serverDB, ctx.userId),
|
37
37
|
fileModel: new FileModel(ctx.serverDB, ctx.userId),
|
38
|
-
fileService: new FileService(),
|
38
|
+
fileService: new FileService(ctx.serverDB, ctx.userId),
|
39
39
|
},
|
40
40
|
});
|
41
41
|
});
|
@@ -19,7 +19,7 @@ const fileProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
|
|
19
19
|
asyncTaskModel: new AsyncTaskModel(ctx.serverDB, ctx.userId),
|
20
20
|
chunkModel: new ChunkModel(ctx.serverDB, ctx.userId),
|
21
21
|
fileModel: new FileModel(ctx.serverDB, ctx.userId),
|
22
|
-
fileService: new FileService(),
|
22
|
+
fileService: new FileService(ctx.serverDB, ctx.userId),
|
23
23
|
},
|
24
24
|
});
|
25
25
|
});
|
@@ -10,10 +10,12 @@ import { ImportResultData, ImporterEntryData } from '@/types/importer';
|
|
10
10
|
|
11
11
|
const importProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
|
12
12
|
const { ctx } = opts;
|
13
|
-
const dataImporterService = new DataImporterRepos(ctx.serverDB, ctx.userId);
|
14
13
|
|
15
14
|
return opts.next({
|
16
|
-
ctx: {
|
15
|
+
ctx: {
|
16
|
+
dataImporterService: new DataImporterRepos(ctx.serverDB, ctx.userId),
|
17
|
+
fileService: new FileService(ctx.serverDB, ctx.userId),
|
18
|
+
},
|
17
19
|
});
|
18
20
|
});
|
19
21
|
|
@@ -16,7 +16,7 @@ const messageProcedure = authedProcedure.use(serverDatabase).use(async (opts) =>
|
|
16
16
|
|
17
17
|
return opts.next({
|
18
18
|
ctx: {
|
19
|
-
fileService: new FileService(),
|
19
|
+
fileService: new FileService(ctx.serverDB, ctx.userId),
|
20
20
|
messageModel: new MessageModel(ctx.serverDB, ctx.userId),
|
21
21
|
},
|
22
22
|
});
|
@@ -102,7 +102,7 @@ export const messageRouter = router({
|
|
102
102
|
const serverDB = await getServerDB();
|
103
103
|
|
104
104
|
const messageModel = new MessageModel(serverDB, ctx.userId);
|
105
|
-
const fileService = new FileService();
|
105
|
+
const fileService = new FileService(serverDB, ctx.userId);
|
106
106
|
|
107
107
|
return messageModel.query(input, {
|
108
108
|
postProcessUrl: (path) => fileService.getFullFileUrl(path),
|
@@ -40,7 +40,7 @@ const ragEvalProcedure = authedProcedure
|
|
40
40
|
datasetRecordModel: new EvalDatasetRecordModel(ctx.userId),
|
41
41
|
evaluationModel: new EvalEvaluationModel(ctx.userId),
|
42
42
|
evaluationRecordModel: new EvaluationRecordModel(ctx.userId),
|
43
|
-
fileService: new FileService(),
|
43
|
+
fileService: new FileService(ctx.serverDB, ctx.userId),
|
44
44
|
},
|
45
45
|
});
|
46
46
|
});
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { UserJSON } from '@clerk/backend';
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
2
3
|
import { z } from 'zod';
|
3
|
-
import { v4 as uuidv4 } from 'uuid'; // 需要添加此导入
|
4
4
|
|
5
5
|
import { enableClerk } from '@/const/auth';
|
6
6
|
import { isDesktop } from '@/const/version';
|
@@ -28,7 +28,7 @@ const userProcedure = authedProcedure.use(serverDatabase).use(async ({ ctx, next
|
|
28
28
|
return next({
|
29
29
|
ctx: {
|
30
30
|
clerkAuth: new ClerkAuth(),
|
31
|
-
fileService: new FileService(),
|
31
|
+
fileService: new FileService(ctx.serverDB, ctx.userId),
|
32
32
|
nextAuthDbAdapter: LobeNextAuthDbAdapter(ctx.serverDB),
|
33
33
|
userModel: new UserModel(ctx.serverDB, ctx.userId),
|
34
34
|
},
|
@@ -1,3 +1,12 @@
|
|
1
|
+
import { TRPCError } from '@trpc/server';
|
2
|
+
|
3
|
+
import { serverDBEnv } from '@/config/db';
|
4
|
+
import { FileModel } from '@/database/models/file';
|
5
|
+
import { FileItem } from '@/database/schemas';
|
6
|
+
import { LobeChatDatabase } from '@/database/type';
|
7
|
+
import { TempFileManager } from '@/server/utils/tempFileManager';
|
8
|
+
import { nanoid } from '@/utils/uuid';
|
9
|
+
|
1
10
|
import { FileServiceImpl, createFileServiceModule } from './impls';
|
2
11
|
|
3
12
|
/**
|
@@ -5,8 +14,16 @@ import { FileServiceImpl, createFileServiceModule } from './impls';
|
|
5
14
|
* 使用模块化实现方式,提供文件操作服务
|
6
15
|
*/
|
7
16
|
export class FileService {
|
17
|
+
private userId: string;
|
18
|
+
private fileModel: FileModel;
|
19
|
+
|
8
20
|
private impl: FileServiceImpl = createFileServiceModule();
|
9
21
|
|
22
|
+
constructor(db: LobeChatDatabase, userId: string) {
|
23
|
+
this.userId = userId;
|
24
|
+
this.fileModel = new FileModel(db, userId);
|
25
|
+
}
|
26
|
+
|
10
27
|
/**
|
11
28
|
* 删除文件
|
12
29
|
*/
|
@@ -62,4 +79,33 @@ export class FileService {
|
|
62
79
|
public async getFullFileUrl(url?: string | null, expiresIn?: number): Promise<string> {
|
63
80
|
return this.impl.getFullFileUrl(url, expiresIn);
|
64
81
|
}
|
82
|
+
|
83
|
+
async downloadFileToLocal(
|
84
|
+
fileId: string,
|
85
|
+
): Promise<{ cleanup: () => void; file: FileItem; filePath: string }> {
|
86
|
+
const file = await this.fileModel.findById(fileId);
|
87
|
+
if (!file) {
|
88
|
+
throw new TRPCError({ code: 'BAD_REQUEST', message: 'File not found' });
|
89
|
+
}
|
90
|
+
|
91
|
+
let content: Uint8Array | undefined;
|
92
|
+
try {
|
93
|
+
content = await this.getFileByteArray(file.url);
|
94
|
+
} catch (e) {
|
95
|
+
console.error(e);
|
96
|
+
// if file not found, delete it from db
|
97
|
+
if ((e as any).Code === 'NoSuchKey') {
|
98
|
+
await this.fileModel.delete(fileId, serverDBEnv.REMOVE_GLOBAL_FILE);
|
99
|
+
throw new TRPCError({ code: 'BAD_REQUEST', message: 'File not found' });
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
if (!content) throw new TRPCError({ code: 'BAD_REQUEST', message: 'File content is empty' });
|
104
|
+
|
105
|
+
const dir = nanoid();
|
106
|
+
const tempManager = new TempFileManager(dir);
|
107
|
+
|
108
|
+
const filePath = await tempManager.writeTempFile(content, file.name);
|
109
|
+
return { cleanup: () => tempManager.cleanup(), file, filePath };
|
110
|
+
}
|
65
111
|
}
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
2
2
|
import { tmpdir } from 'node:os';
|
3
3
|
import { join } from 'node:path';
|
4
|
-
import { v4 as uuidv4 } from 'uuid';
|
5
4
|
|
6
5
|
/**
|
7
6
|
* 安全存储临时文件工具类
|
@@ -10,21 +9,19 @@ export class TempFileManager {
|
|
10
9
|
private readonly tempDir: string;
|
11
10
|
private filePaths: Set<string> = new Set();
|
12
11
|
|
13
|
-
constructor() {
|
12
|
+
constructor(dirname: string) {
|
14
13
|
// 创建唯一临时目录 (跨平台安全)
|
15
|
-
this.tempDir = mkdtempSync(join(tmpdir(),
|
14
|
+
this.tempDir = mkdtempSync(join(tmpdir(), dirname));
|
16
15
|
// 注册退出清理钩子
|
17
16
|
this.registerCleanupHook();
|
18
17
|
}
|
19
18
|
|
20
19
|
/**
|
21
20
|
* 将 Uint8Array 写入临时文件
|
22
|
-
|
23
|
-
* @param ext 文件扩展名 (默认 .epub)
|
24
|
-
* @returns 临时文件绝对路径
|
21
|
+
|
25
22
|
*/
|
26
|
-
async writeTempFile(data: Uint8Array,
|
27
|
-
const filePath = join(this.tempDir,
|
23
|
+
async writeTempFile(data: Uint8Array, name: string): Promise<string> {
|
24
|
+
const filePath = join(this.tempDir, name);
|
28
25
|
|
29
26
|
try {
|
30
27
|
writeFileSync(filePath, data);
|