@lobehub/chat 1.12.17 → 1.12.19
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 +50 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +2 -2
- package/src/database/server/models/session.ts +2 -1
- package/src/layout/GlobalProvider/Locale.tsx +11 -1
- package/src/libs/agent-runtime/groq/index.test.ts +1 -1
- package/src/libs/agent-runtime/qwen/index.test.ts +2 -2
- package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.test.ts +28 -22
- package/src/libs/trpc/index.ts +2 -2
- package/src/libs/trpc/middleware/{password.test.ts → jwtPayload.test.ts} +3 -22
- package/src/libs/trpc/middleware/jwtPayload.ts +14 -0
- package/src/libs/trpc/middleware/password.ts +0 -26
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 1.12.19](https://github.com/lobehub/lobe-chat/compare/v1.12.18...v1.12.19)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2024-08-25**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: Fix cannot clone agent when imported from client.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: Fix cannot clone agent when imported from client, closes [#3606](https://github.com/lobehub/lobe-chat/issues/3606) ([1fd2fa0](https://github.com/lobehub/lobe-chat/commit/1fd2fa0))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
### [Version 1.12.18](https://github.com/lobehub/lobe-chat/compare/v1.12.17...v1.12.18)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2024-08-25**</sup>
|
|
33
|
+
|
|
34
|
+
#### 🐛 Bug Fixes
|
|
35
|
+
|
|
36
|
+
- **misc**: Fix dayjs error in en-US language.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### What's fixed
|
|
44
|
+
|
|
45
|
+
- **misc**: Fix dayjs error in en-US language, closes [#3604](https://github.com/lobehub/lobe-chat/issues/3604) ([174f4df](https://github.com/lobehub/lobe-chat/commit/174f4df))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
### [Version 1.12.17](https://github.com/lobehub/lobe-chat/compare/v1.12.16...v1.12.17)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2024-08-25**</sup>
|
package/README.md
CHANGED
|
@@ -273,7 +273,7 @@ Our marketplace is not just a showcase platform but also a collaborative space.
|
|
|
273
273
|
| [Prompt Engineering Expert](https://chat-preview.lobehub.com/market?agent=ai-prompts-assistant)<br/><sup>By **[cyicz123](https://github.com/cyicz123)** on **2024-08-12**</sup> | Specializing in prompt optimization and design<br/>`prompt-engineering` `ai-interaction` `writing` `optimization` `consulting` |
|
|
274
274
|
| [Commit Message Generator](https://chat-preview.lobehub.com/market?agent=commit-assistant)<br/><sup>By **[cyicz123](https://github.com/cyicz123)** on **2024-08-12**</sup> | Expert at generating precise Git commit messages<br/>`programming` `git` `commit-message` `code-review` |
|
|
275
275
|
|
|
276
|
-
> 📊 Total agents: [<kbd>**
|
|
276
|
+
> 📊 Total agents: [<kbd>**317**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
277
277
|
|
|
278
278
|
<!-- AGENT LIST -->
|
|
279
279
|
|
package/README.zh-CN.md
CHANGED
|
@@ -261,7 +261,7 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
|
|
261
261
|
| [提示工程专家](https://chat-preview.lobehub.com/market?agent=ai-prompts-assistant)<br/><sup>By **[cyicz123](https://github.com/cyicz123)** on **2024-08-12**</sup> | 专精 Prompt 优化与设计<br/>`提示工程` `ai交互` `写作` `优化` `咨询` |
|
|
262
262
|
| [提交信息生成器](https://chat-preview.lobehub.com/market?agent=commit-assistant)<br/><sup>By **[cyicz123](https://github.com/cyicz123)** on **2024-08-12**</sup> | 擅长生成精准的 Git 提交信息<br/>`编程` `git` `提交信息` `代码审查` |
|
|
263
263
|
|
|
264
|
-
> 📊 Total agents: [<kbd>**
|
|
264
|
+
> 📊 Total agents: [<kbd>**317**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
265
265
|
|
|
266
266
|
<!-- AGENT LIST -->
|
|
267
267
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.19",
|
|
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",
|
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
"nuqs": "^1.17.8",
|
|
172
172
|
"officeparser": "^4.1.1",
|
|
173
173
|
"ollama": "^0.5.8",
|
|
174
|
-
"openai": "
|
|
174
|
+
"openai": "^4.56.0",
|
|
175
175
|
"partial-json": "^0.1.7",
|
|
176
176
|
"pdf-parse": "^1.1.1",
|
|
177
177
|
"pdfjs-dist": "4.4.168",
|
|
@@ -170,7 +170,8 @@ export class SessionModel {
|
|
|
170
170
|
|
|
171
171
|
if (!result) return;
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
174
|
+
const { agent, clientId, ...session } = result;
|
|
174
175
|
const sessionId = this.genId();
|
|
175
176
|
|
|
176
177
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -37,7 +37,17 @@ const Locale = memo<LocaleLayoutProps>(({ children, defaultLang, antdLocale }) =
|
|
|
37
37
|
if (!lang) return;
|
|
38
38
|
|
|
39
39
|
// load default lang
|
|
40
|
-
|
|
40
|
+
let dayJSLocale;
|
|
41
|
+
try {
|
|
42
|
+
// dayjs locale is using `en` instead of `en-US`
|
|
43
|
+
// refs: https://github.com/lobehub/lobe-chat/issues/3396
|
|
44
|
+
const locale = lang!.toLowerCase() === 'en-us' ? 'en' : lang!.toLowerCase();
|
|
45
|
+
|
|
46
|
+
dayJSLocale = await import(`dayjs/locale/${locale}.js`);
|
|
47
|
+
} catch {
|
|
48
|
+
console.warn(`dayjs locale for ${lang} not found, fallback to en`);
|
|
49
|
+
dayJSLocale = await import(`dayjs/locale/en.js`);
|
|
50
|
+
}
|
|
41
51
|
|
|
42
52
|
dayjs.locale(dayJSLocale.default);
|
|
43
53
|
});
|
|
@@ -106,7 +106,7 @@ describe('LobeQwenAI', () => {
|
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
it('should transform non-streaming response to stream correctly', async () => {
|
|
109
|
-
const mockResponse
|
|
109
|
+
const mockResponse = {
|
|
110
110
|
id: 'chatcmpl-fc539f49-51a8-94be-8061',
|
|
111
111
|
object: 'chat.completion',
|
|
112
112
|
created: 1719901794,
|
|
@@ -119,7 +119,7 @@ describe('LobeQwenAI', () => {
|
|
|
119
119
|
logprobs: null,
|
|
120
120
|
},
|
|
121
121
|
],
|
|
122
|
-
};
|
|
122
|
+
} as OpenAI.ChatCompletion;
|
|
123
123
|
vi.spyOn(instance['client'].chat.completions, 'create').mockResolvedValue(
|
|
124
124
|
mockResponse as any,
|
|
125
125
|
);
|
|
@@ -342,7 +342,7 @@ describe('LobeOpenAICompatibleFactory', () => {
|
|
|
342
342
|
});
|
|
343
343
|
|
|
344
344
|
it('should transform non-streaming response to stream correctly', async () => {
|
|
345
|
-
const mockResponse
|
|
345
|
+
const mockResponse = {
|
|
346
346
|
id: 'a',
|
|
347
347
|
object: 'chat.completion',
|
|
348
348
|
created: 123,
|
|
@@ -360,7 +360,7 @@ describe('LobeOpenAICompatibleFactory', () => {
|
|
|
360
360
|
completion_tokens: 5,
|
|
361
361
|
total_tokens: 10,
|
|
362
362
|
},
|
|
363
|
-
};
|
|
363
|
+
} as OpenAI.ChatCompletion;
|
|
364
364
|
vi.spyOn(instance['client'].chat.completions, 'create').mockResolvedValue(
|
|
365
365
|
mockResponse as any,
|
|
366
366
|
);
|
|
@@ -426,27 +426,29 @@ describe('LobeOpenAICompatibleFactory', () => {
|
|
|
426
426
|
},
|
|
427
427
|
provider: ModelProvider.Mistral,
|
|
428
428
|
});
|
|
429
|
-
|
|
429
|
+
|
|
430
430
|
const instance = new LobeMockProvider({ apiKey: 'test' });
|
|
431
|
-
const mockCreateMethod = vi
|
|
432
|
-
|
|
431
|
+
const mockCreateMethod = vi
|
|
432
|
+
.spyOn(instance['client'].chat.completions, 'create')
|
|
433
|
+
.mockResolvedValue(new ReadableStream() as any);
|
|
434
|
+
|
|
433
435
|
await instance.chat(
|
|
434
436
|
{
|
|
435
437
|
messages: [{ content: 'Hello', role: 'user' }],
|
|
436
438
|
model: 'open-mistral-7b',
|
|
437
439
|
temperature: 0,
|
|
438
440
|
},
|
|
439
|
-
{ user: 'testUser' }
|
|
441
|
+
{ user: 'testUser' },
|
|
440
442
|
);
|
|
441
|
-
|
|
443
|
+
|
|
442
444
|
expect(mockCreateMethod).toHaveBeenCalledWith(
|
|
443
445
|
expect.not.objectContaining({
|
|
444
446
|
user: 'testUser',
|
|
445
447
|
}),
|
|
446
|
-
expect.anything()
|
|
448
|
+
expect.anything(),
|
|
447
449
|
);
|
|
448
450
|
});
|
|
449
|
-
|
|
451
|
+
|
|
450
452
|
it('should add user to payload when noUserId is false', async () => {
|
|
451
453
|
const LobeMockProvider = LobeOpenAICompatibleFactory({
|
|
452
454
|
baseURL: 'https://api.mistral.ai/v1',
|
|
@@ -455,50 +457,54 @@ describe('LobeOpenAICompatibleFactory', () => {
|
|
|
455
457
|
},
|
|
456
458
|
provider: ModelProvider.Mistral,
|
|
457
459
|
});
|
|
458
|
-
|
|
460
|
+
|
|
459
461
|
const instance = new LobeMockProvider({ apiKey: 'test' });
|
|
460
|
-
const mockCreateMethod = vi
|
|
461
|
-
|
|
462
|
+
const mockCreateMethod = vi
|
|
463
|
+
.spyOn(instance['client'].chat.completions, 'create')
|
|
464
|
+
.mockResolvedValue(new ReadableStream() as any);
|
|
465
|
+
|
|
462
466
|
await instance.chat(
|
|
463
467
|
{
|
|
464
468
|
messages: [{ content: 'Hello', role: 'user' }],
|
|
465
469
|
model: 'open-mistral-7b',
|
|
466
470
|
temperature: 0,
|
|
467
471
|
},
|
|
468
|
-
{ user: 'testUser' }
|
|
472
|
+
{ user: 'testUser' },
|
|
469
473
|
);
|
|
470
|
-
|
|
474
|
+
|
|
471
475
|
expect(mockCreateMethod).toHaveBeenCalledWith(
|
|
472
476
|
expect.objectContaining({
|
|
473
477
|
user: 'testUser',
|
|
474
478
|
}),
|
|
475
|
-
expect.anything()
|
|
479
|
+
expect.anything(),
|
|
476
480
|
);
|
|
477
481
|
});
|
|
478
|
-
|
|
482
|
+
|
|
479
483
|
it('should add user to payload when noUserId is not set in chatCompletion', async () => {
|
|
480
484
|
const LobeMockProvider = LobeOpenAICompatibleFactory({
|
|
481
485
|
baseURL: 'https://api.mistral.ai/v1',
|
|
482
486
|
provider: ModelProvider.Mistral,
|
|
483
487
|
});
|
|
484
|
-
|
|
488
|
+
|
|
485
489
|
const instance = new LobeMockProvider({ apiKey: 'test' });
|
|
486
|
-
const mockCreateMethod = vi
|
|
487
|
-
|
|
490
|
+
const mockCreateMethod = vi
|
|
491
|
+
.spyOn(instance['client'].chat.completions, 'create')
|
|
492
|
+
.mockResolvedValue(new ReadableStream() as any);
|
|
493
|
+
|
|
488
494
|
await instance.chat(
|
|
489
495
|
{
|
|
490
496
|
messages: [{ content: 'Hello', role: 'user' }],
|
|
491
497
|
model: 'open-mistral-7b',
|
|
492
498
|
temperature: 0,
|
|
493
499
|
},
|
|
494
|
-
{ user: 'testUser' }
|
|
500
|
+
{ user: 'testUser' },
|
|
495
501
|
);
|
|
496
|
-
|
|
502
|
+
|
|
497
503
|
expect(mockCreateMethod).toHaveBeenCalledWith(
|
|
498
504
|
expect.objectContaining({
|
|
499
505
|
user: 'testUser',
|
|
500
506
|
}),
|
|
501
|
-
expect.anything()
|
|
507
|
+
expect.anything(),
|
|
502
508
|
);
|
|
503
509
|
});
|
|
504
510
|
});
|
package/src/libs/trpc/index.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @link https://trpc.io/docs/v11/procedures
|
|
9
9
|
*/
|
|
10
10
|
import { trpc } from './init';
|
|
11
|
-
import {
|
|
11
|
+
import { jwtPayloadChecker } from './middleware/jwtPayload';
|
|
12
12
|
import { userAuth } from './middleware/userAuth';
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -27,7 +27,7 @@ export const publicProcedure = trpc.procedure;
|
|
|
27
27
|
export const authedProcedure = trpc.procedure.use(userAuth);
|
|
28
28
|
|
|
29
29
|
// procedure that asserts that the user add the password
|
|
30
|
-
export const passwordProcedure = trpc.procedure.use(
|
|
30
|
+
export const passwordProcedure = trpc.procedure.use(jwtPayloadChecker);
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Merge multiple routers together
|
|
@@ -3,15 +3,14 @@ import { TRPCError } from '@trpc/server';
|
|
|
3
3
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
4
|
|
|
5
5
|
import * as utils from '@/app/api/middleware/auth/utils';
|
|
6
|
-
import * as appConfig from '@/config/app';
|
|
7
6
|
import { createCallerFactory } from '@/libs/trpc';
|
|
8
7
|
import { trpc } from '@/libs/trpc/init';
|
|
9
8
|
import { AuthContext, createContextInner } from '@/server/context';
|
|
10
9
|
|
|
11
|
-
import {
|
|
10
|
+
import { jwtPayloadChecker } from './jwtPayload';
|
|
12
11
|
|
|
13
12
|
const appRouter = trpc.router({
|
|
14
|
-
protectedQuery: trpc.procedure.use(
|
|
13
|
+
protectedQuery: trpc.procedure.use(jwtPayloadChecker).query(async ({ ctx }) => {
|
|
15
14
|
return ctx.jwtPayload;
|
|
16
15
|
}),
|
|
17
16
|
});
|
|
@@ -38,23 +37,9 @@ describe('passwordChecker middleware', () => {
|
|
|
38
37
|
await expect(router.protectedQuery()).rejects.toThrow(new TRPCError({ code: 'UNAUTHORIZED' }));
|
|
39
38
|
});
|
|
40
39
|
|
|
41
|
-
it('should throw UNAUTHORIZED error if access code is not correct', async () => {
|
|
42
|
-
vi.spyOn(appConfig, 'getAppConfig').mockReturnValue({
|
|
43
|
-
ACCESS_CODES: ['123'],
|
|
44
|
-
} as any);
|
|
45
|
-
vi.spyOn(utils, 'getJWTPayload').mockResolvedValue({ accessCode: '456' });
|
|
46
|
-
|
|
47
|
-
ctx = await createContextInner({ authorizationHeader: 'Bearer token' });
|
|
48
|
-
router = createCaller(ctx);
|
|
49
|
-
|
|
50
|
-
await expect(router.protectedQuery()).rejects.toThrow(new TRPCError({ code: 'UNAUTHORIZED' }));
|
|
51
|
-
});
|
|
52
|
-
|
|
53
40
|
it('should call next with jwtPayload in context if access code is correct', async () => {
|
|
54
41
|
const jwtPayload = { accessCode: '123' };
|
|
55
|
-
|
|
56
|
-
ACCESS_CODES: ['123'],
|
|
57
|
-
} as any);
|
|
42
|
+
|
|
58
43
|
vi.spyOn(utils, 'getJWTPayload').mockResolvedValue(jwtPayload);
|
|
59
44
|
|
|
60
45
|
ctx = await createContextInner({ authorizationHeader: 'Bearer token' });
|
|
@@ -67,9 +52,6 @@ describe('passwordChecker middleware', () => {
|
|
|
67
52
|
|
|
68
53
|
it('should call next with jwtPayload in context if no access codes are set', async () => {
|
|
69
54
|
const jwtPayload = {};
|
|
70
|
-
vi.spyOn(appConfig, 'getAppConfig').mockReturnValue({
|
|
71
|
-
ACCESS_CODES: [],
|
|
72
|
-
} as any);
|
|
73
55
|
vi.spyOn(utils, 'getJWTPayload').mockResolvedValue(jwtPayload);
|
|
74
56
|
|
|
75
57
|
ctx = await createContextInner({ authorizationHeader: 'Bearer token' });
|
|
@@ -81,7 +63,6 @@ describe('passwordChecker middleware', () => {
|
|
|
81
63
|
});
|
|
82
64
|
it('should call next with jwtPayload in context if access codes is undefined', async () => {
|
|
83
65
|
const jwtPayload = {};
|
|
84
|
-
vi.spyOn(appConfig, 'getAppConfig').mockReturnValue({} as any);
|
|
85
66
|
vi.spyOn(utils, 'getJWTPayload').mockResolvedValue(jwtPayload);
|
|
86
67
|
|
|
87
68
|
ctx = await createContextInner({ authorizationHeader: 'Bearer token' });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { TRPCError } from '@trpc/server';
|
|
2
|
+
|
|
3
|
+
import { getJWTPayload } from '@/app/api/middleware/auth/utils';
|
|
4
|
+
import { trpc } from '@/libs/trpc/init';
|
|
5
|
+
|
|
6
|
+
export const jwtPayloadChecker = trpc.middleware(async (opts) => {
|
|
7
|
+
const { ctx } = opts;
|
|
8
|
+
|
|
9
|
+
if (!ctx.authorizationHeader) throw new TRPCError({ code: 'UNAUTHORIZED' });
|
|
10
|
+
|
|
11
|
+
const jwtPayload = await getJWTPayload(ctx.authorizationHeader);
|
|
12
|
+
|
|
13
|
+
return opts.next({ ctx: { jwtPayload } });
|
|
14
|
+
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { TRPCError } from '@trpc/server';
|
|
2
|
-
|
|
3
|
-
import { getJWTPayload } from '@/app/api/middleware/auth/utils';
|
|
4
|
-
import { getAppConfig } from '@/config/app';
|
|
5
|
-
import { trpc } from '@/libs/trpc/init';
|
|
6
|
-
|
|
7
|
-
export const passwordChecker = trpc.middleware(async (opts) => {
|
|
8
|
-
const { ACCESS_CODES } = getAppConfig();
|
|
9
|
-
|
|
10
|
-
const { ctx } = opts;
|
|
11
|
-
|
|
12
|
-
if (!ctx.authorizationHeader) throw new TRPCError({ code: 'UNAUTHORIZED' });
|
|
13
|
-
|
|
14
|
-
const jwtPayload = await getJWTPayload(ctx.authorizationHeader);
|
|
15
|
-
|
|
16
|
-
// if there are access codes, check if the user has set correct one
|
|
17
|
-
if (ACCESS_CODES && ACCESS_CODES.length > 0) {
|
|
18
|
-
const accessCode = jwtPayload.accessCode;
|
|
19
|
-
|
|
20
|
-
if (!accessCode || !ACCESS_CODES.includes(accessCode)) {
|
|
21
|
-
throw new TRPCError({ code: 'UNAUTHORIZED' });
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return opts.next({ ctx: { jwtPayload } });
|
|
26
|
-
});
|