@lobehub/chat 1.87.9 → 1.88.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.
- package/CHANGELOG.md +67 -0
- package/README.md +8 -8
- package/README.zh-CN.md +8 -8
- package/changelog/v1.json +24 -0
- package/package.json +1 -1
- package/src/config/aiModels/anthropic.ts +49 -1
- package/src/config/aiModels/google.ts +2 -2
- package/src/const/models.ts +8 -0
- package/src/database/models/__tests__/session.test.ts +173 -17
- package/src/database/models/session.ts +14 -4
- package/src/libs/model-runtime/google/index.ts +9 -0
- package/src/libs/model-runtime/utils/streams/google-ai.ts +9 -0
- package/src/server/routers/lambda/session.ts +3 -19
- package/src/server/services/agent/index.test.ts +3 -0
- package/src/server/services/agent/index.ts +2 -5
- package/src/services/session/client.ts +1 -4
- package/src/store/user/slices/auth/selectors.test.ts +39 -2
- package/src/store/user/slices/auth/selectors.ts +15 -3
- package/src/store/user/slices/common/action.test.ts +2 -0
- package/src/store/user/slices/common/action.ts +1 -0
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,73 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.88.1](https://github.com/lobehub/lobe-chat/compare/v1.88.0...v1.88.1)
|
6
|
+
|
7
|
+
<sup>Released on **2025-05-24**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: User nickName & username selector in desktop.
|
12
|
+
|
13
|
+
#### 💄 Styles
|
14
|
+
|
15
|
+
- **misc**: Support Gemini 2.5 thought reasoning.
|
16
|
+
|
17
|
+
<br/>
|
18
|
+
|
19
|
+
<details>
|
20
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
21
|
+
|
22
|
+
#### What's fixed
|
23
|
+
|
24
|
+
- **misc**: User nickName & username selector in desktop, closes [#7899](https://github.com/lobehub/lobe-chat/issues/7899) ([bf51746](https://github.com/lobehub/lobe-chat/commit/bf51746))
|
25
|
+
|
26
|
+
#### Styles
|
27
|
+
|
28
|
+
- **misc**: Support Gemini 2.5 thought reasoning, closes [#7686](https://github.com/lobehub/lobe-chat/issues/7686) ([f34c4de](https://github.com/lobehub/lobe-chat/commit/f34c4de))
|
29
|
+
|
30
|
+
</details>
|
31
|
+
|
32
|
+
<div align="right">
|
33
|
+
|
34
|
+
[](#readme-top)
|
35
|
+
|
36
|
+
</div>
|
37
|
+
|
38
|
+
## [Version 1.88.0](https://github.com/lobehub/lobe-chat/compare/v1.87.9...v1.88.0)
|
39
|
+
|
40
|
+
<sup>Released on **2025-05-23**</sup>
|
41
|
+
|
42
|
+
#### ✨ Features
|
43
|
+
|
44
|
+
- **misc**: Add claude 4 series.
|
45
|
+
|
46
|
+
#### 🐛 Bug Fixes
|
47
|
+
|
48
|
+
- **misc**: Fix missing email field to user, update agent config of client db will override old config.
|
49
|
+
|
50
|
+
<br/>
|
51
|
+
|
52
|
+
<details>
|
53
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
54
|
+
|
55
|
+
#### What's improved
|
56
|
+
|
57
|
+
- **misc**: Add claude 4 series, closes [#7939](https://github.com/lobehub/lobe-chat/issues/7939) ([9b4f950](https://github.com/lobehub/lobe-chat/commit/9b4f950))
|
58
|
+
|
59
|
+
#### What's fixed
|
60
|
+
|
61
|
+
- **misc**: Fix missing email field to user, closes [#7913](https://github.com/lobehub/lobe-chat/issues/7913) ([d314130](https://github.com/lobehub/lobe-chat/commit/d314130))
|
62
|
+
- **misc**: Update agent config of client db will override old config, closes [#7918](https://github.com/lobehub/lobe-chat/issues/7918) ([f7cda68](https://github.com/lobehub/lobe-chat/commit/f7cda68))
|
63
|
+
|
64
|
+
</details>
|
65
|
+
|
66
|
+
<div align="right">
|
67
|
+
|
68
|
+
[](#readme-top)
|
69
|
+
|
70
|
+
</div>
|
71
|
+
|
5
72
|
### [Version 1.87.9](https://github.com/lobehub/lobe-chat/compare/v1.87.8...v1.87.9)
|
6
73
|
|
7
74
|
<sup>Released on **2025-05-23**</sup>
|
package/README.md
CHANGED
@@ -327,14 +327,14 @@ In addition, these plugins are not limited to news aggregation, but can also ext
|
|
327
327
|
|
328
328
|
<!-- PLUGIN LIST -->
|
329
329
|
|
330
|
-
| Recent Submits
|
331
|
-
|
|
332
|
-
| [
|
333
|
-
| [
|
334
|
-
| [
|
335
|
-
| [
|
336
|
-
|
337
|
-
> 📊 Total plugins: [<kbd>**
|
330
|
+
| Recent Submits | Description |
|
331
|
+
| ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
332
|
+
| [Web](https://lobechat.com/discover/plugin/web)<br/><sup>By **Proghit** on **2025-01-24**</sup> | Smart web search that reads and analyzes pages to deliver comprehensive answers from Google results.<br/>`web` `search` |
|
333
|
+
| [Bing_websearch](https://lobechat.com/discover/plugin/Bingsearch-identifier)<br/><sup>By **FineHow** on **2024-12-22**</sup> | Search for information from the internet base BingApi<br/>`bingsearch` |
|
334
|
+
| [Google CSE](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | Searches Google through their official CSE API.<br/>`web` `search` |
|
335
|
+
| [Tongyi wanxiang Image Generator](https://lobechat.com/discover/plugin/alps-tongyi-image)<br/><sup>By **YoungTx** on **2024-08-09**</sup> | This plugin uses Alibaba's Tongyi Wanxiang model to generate images based on text prompts.<br/>`image` `tongyi` `wanxiang` |
|
336
|
+
|
337
|
+
> 📊 Total plugins: [<kbd>**42**</kbd>](https://lobechat.com/discover/plugins)
|
338
338
|
|
339
339
|
<!-- PLUGIN LIST -->
|
340
340
|
|
package/README.zh-CN.md
CHANGED
@@ -320,14 +320,14 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
|
320
320
|
|
321
321
|
<!-- PLUGIN LIST -->
|
322
322
|
|
323
|
-
| 最近新增
|
324
|
-
|
|
325
|
-
| [
|
326
|
-
| [
|
327
|
-
| [
|
328
|
-
| [
|
329
|
-
|
330
|
-
> 📊 Total plugins: [<kbd>**
|
323
|
+
| 最近新增 | 描述 |
|
324
|
+
| ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
325
|
+
| [网页](https://lobechat.com/discover/plugin/web)<br/><sup>By **Proghit** on **2025-01-24**</sup> | 智能网页搜索,读取和分析页面,以提供来自 Google 结果的全面答案。<br/>`网页` `搜索` |
|
326
|
+
| [必应网页搜索](https://lobechat.com/discover/plugin/Bingsearch-identifier)<br/><sup>By **FineHow** on **2024-12-22**</sup> | 通过 BingApi 搜索互联网上的信息<br/>`bingsearch` |
|
327
|
+
| [谷歌自定义搜索引擎](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | 通过他们的官方自定义搜索引擎 API 搜索谷歌。<br/>`网络` `搜索` |
|
328
|
+
| [通义万象图像生成器](https://lobechat.com/discover/plugin/alps-tongyi-image)<br/><sup>By **YoungTx** on **2024-08-09**</sup> | 此插件使用阿里巴巴的通义万象模型根据文本提示生成图像。<br/>`图像` `通义` `万象` |
|
329
|
+
|
330
|
+
> 📊 Total plugins: [<kbd>**42**</kbd>](https://lobechat.com/discover/plugins)
|
331
331
|
|
332
332
|
<!-- PLUGIN LIST -->
|
333
333
|
|
package/changelog/v1.json
CHANGED
@@ -1,4 +1,28 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"children": {
|
4
|
+
"fixes": [
|
5
|
+
"User nickName & username selector in desktop."
|
6
|
+
],
|
7
|
+
"improvements": [
|
8
|
+
"Support Gemini 2.5 thought reasoning."
|
9
|
+
]
|
10
|
+
},
|
11
|
+
"date": "2025-05-24",
|
12
|
+
"version": "1.88.1"
|
13
|
+
},
|
14
|
+
{
|
15
|
+
"children": {
|
16
|
+
"features": [
|
17
|
+
"Add claude 4 series."
|
18
|
+
],
|
19
|
+
"fixes": [
|
20
|
+
"Fix missing email field to user, update agent config of client db will override old config."
|
21
|
+
]
|
22
|
+
},
|
23
|
+
"date": "2025-05-23",
|
24
|
+
"version": "1.88.0"
|
25
|
+
},
|
2
26
|
{
|
3
27
|
"children": {
|
4
28
|
"improvements": [
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.88.1",
|
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",
|
@@ -1,6 +1,55 @@
|
|
1
1
|
import { AIChatModelCard } from '@/types/aiModel';
|
2
2
|
|
3
3
|
const anthropicChatModels: AIChatModelCard[] = [
|
4
|
+
{
|
5
|
+
abilities: {
|
6
|
+
functionCall: true,
|
7
|
+
reasoning: true,
|
8
|
+
vision: true,
|
9
|
+
},
|
10
|
+
contextWindowTokens: 200_000,
|
11
|
+
description:
|
12
|
+
'Claude 4 Sonnet 可以产生近乎即时的响应或延长的逐步思考,用户可以清晰地看到这些过程。API 用户还可以对模型思考的时间进行细致的控制',
|
13
|
+
displayName: 'Claude 4 Sonnet',
|
14
|
+
enabled: true,
|
15
|
+
id: 'claude-sonnet-4-20250514',
|
16
|
+
maxOutput: 8192,
|
17
|
+
pricing: {
|
18
|
+
cachedInput: 0.3,
|
19
|
+
input: 3,
|
20
|
+
output: 15,
|
21
|
+
writeCacheInput: 3.75,
|
22
|
+
},
|
23
|
+
releasedAt: '2025-05-23',
|
24
|
+
settings: {
|
25
|
+
extendParams: ['disableContextCaching', 'enableReasoning', 'reasoningBudgetToken'],
|
26
|
+
},
|
27
|
+
type: 'chat',
|
28
|
+
},
|
29
|
+
{
|
30
|
+
abilities: {
|
31
|
+
functionCall: true,
|
32
|
+
vision: true,
|
33
|
+
},
|
34
|
+
contextWindowTokens: 200_000,
|
35
|
+
description:
|
36
|
+
'Claude Opus 4 是 Anthropic 用于处理高度复杂任务的最强大模型。它在性能、智能、流畅性和理解力方面表现卓越。',
|
37
|
+
displayName: 'Claude Opus 4',
|
38
|
+
enabled: true,
|
39
|
+
id: 'claude-opus-4-20250514',
|
40
|
+
maxOutput: 8192,
|
41
|
+
pricing: {
|
42
|
+
cachedInput: 1.5,
|
43
|
+
input: 15,
|
44
|
+
output: 75,
|
45
|
+
writeCacheInput: 18.75,
|
46
|
+
},
|
47
|
+
releasedAt: '2025-05-23',
|
48
|
+
settings: {
|
49
|
+
extendParams: ['disableContextCaching', 'enableReasoning', 'reasoningBudgetToken'],
|
50
|
+
},
|
51
|
+
type: 'chat',
|
52
|
+
},
|
4
53
|
{
|
5
54
|
abilities: {
|
6
55
|
functionCall: true,
|
@@ -145,7 +194,6 @@ const anthropicChatModels: AIChatModelCard[] = [
|
|
145
194
|
description:
|
146
195
|
'Claude 3 Opus 是 Anthropic 用于处理高度复杂任务的最强大模型。它在性能、智能、流畅性和理解力方面表现卓越。',
|
147
196
|
displayName: 'Claude 3 Opus',
|
148
|
-
enabled: true,
|
149
197
|
id: 'claude-3-opus-20240229',
|
150
198
|
maxOutput: 4096,
|
151
199
|
pricing: {
|
@@ -285,7 +285,7 @@ const googleChatModels: AIChatModelCard[] = [
|
|
285
285
|
contextWindowTokens: 2_008_192,
|
286
286
|
description:
|
287
287
|
'Gemini 1.5 Pro 002 是最新的生产就绪模型,提供更高质量的输出,特别在数学、长上下文和视觉任务方面有显著提升。',
|
288
|
-
displayName: 'Gemini 1.5 Pro 002',
|
288
|
+
displayName: 'Gemini 1.5 Pro 002 (Paid)',
|
289
289
|
id: 'gemini-1.5-pro-002', // Deprecated on 2025-09-24
|
290
290
|
maxOutput: 8192,
|
291
291
|
pricing: {
|
@@ -303,7 +303,7 @@ const googleChatModels: AIChatModelCard[] = [
|
|
303
303
|
},
|
304
304
|
contextWindowTokens: 2_008_192,
|
305
305
|
description: 'Gemini 1.5 Pro 001 是可扩展的多模态AI解决方案,支持广泛的复杂任务。',
|
306
|
-
displayName: 'Gemini 1.5 Pro 001',
|
306
|
+
displayName: 'Gemini 1.5 Pro 001 (Paid)',
|
307
307
|
id: 'gemini-1.5-pro-001', // Deprecated on 2025-05-27
|
308
308
|
maxOutput: 8192,
|
309
309
|
pricing: {
|
package/src/const/models.ts
CHANGED
@@ -12,6 +12,10 @@ export const disableStreamModels = new Set(['o1', 'o1-2024-12-17']);
|
|
12
12
|
* models support context caching
|
13
13
|
*/
|
14
14
|
export const contextCachingModels = new Set([
|
15
|
+
'claude-opus-4-latest',
|
16
|
+
'claude-opus-4-20250514',
|
17
|
+
'claude-sonnet-4-latest',
|
18
|
+
'claude-sonnet-4-20250514',
|
15
19
|
'claude-3-7-sonnet-latest',
|
16
20
|
'claude-3-7-sonnet-20250219',
|
17
21
|
'claude-3-5-sonnet-latest',
|
@@ -22,6 +26,10 @@ export const contextCachingModels = new Set([
|
|
22
26
|
]);
|
23
27
|
|
24
28
|
export const thinkingWithToolClaudeModels = new Set([
|
29
|
+
'claude-opus-4-latest',
|
30
|
+
'claude-opus-4-20250514',
|
31
|
+
'claude-sonnet-4-latest',
|
32
|
+
'claude-sonnet-4-20250514',
|
25
33
|
'claude-3-7-sonnet-latest',
|
26
34
|
'claude-3-7-sonnet-20250219',
|
27
35
|
]);
|
@@ -747,18 +747,35 @@ describe('SessionModel', () => {
|
|
747
747
|
});
|
748
748
|
|
749
749
|
describe('updateConfig', () => {
|
750
|
-
it('should update agent config', async () => {
|
751
|
-
// Create test agent
|
750
|
+
it('should update agent config via sessionId', async () => {
|
751
|
+
// Create test session with agent
|
752
|
+
const sessionId = 'test-session';
|
752
753
|
const agentId = 'test-agent';
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
754
|
+
|
755
|
+
await serverDB.transaction(async (trx) => {
|
756
|
+
await trx.insert(sessions).values({
|
757
|
+
id: sessionId,
|
758
|
+
userId,
|
759
|
+
type: 'agent',
|
760
|
+
});
|
761
|
+
|
762
|
+
await trx.insert(agents).values({
|
763
|
+
id: agentId,
|
764
|
+
userId,
|
765
|
+
model: 'gpt-3.5-turbo',
|
766
|
+
title: 'Original Title',
|
767
|
+
description: 'Original description',
|
768
|
+
});
|
769
|
+
|
770
|
+
await trx.insert(agentsToSessions).values({
|
771
|
+
sessionId,
|
772
|
+
agentId,
|
773
|
+
userId,
|
774
|
+
});
|
758
775
|
});
|
759
776
|
|
760
|
-
// Update config
|
761
|
-
await sessionModel.updateConfig(
|
777
|
+
// Update config using sessionId
|
778
|
+
await sessionModel.updateConfig(sessionId, {
|
762
779
|
model: 'gpt-4',
|
763
780
|
title: 'Updated Title',
|
764
781
|
description: 'New description',
|
@@ -777,23 +794,162 @@ describe('SessionModel', () => {
|
|
777
794
|
});
|
778
795
|
});
|
779
796
|
|
780
|
-
it('should
|
797
|
+
it('should merge config with existing agent config', async () => {
|
798
|
+
// Create test session with agent having existing config
|
799
|
+
const sessionId = 'test-session-merge';
|
800
|
+
const agentId = 'test-agent-merge';
|
801
|
+
|
802
|
+
await serverDB.transaction(async (trx) => {
|
803
|
+
await trx.insert(sessions).values({
|
804
|
+
id: sessionId,
|
805
|
+
userId,
|
806
|
+
type: 'agent',
|
807
|
+
});
|
808
|
+
|
809
|
+
await trx.insert(agents).values({
|
810
|
+
id: agentId,
|
811
|
+
userId,
|
812
|
+
model: 'gpt-3.5-turbo',
|
813
|
+
title: 'Original Title',
|
814
|
+
description: 'Original description',
|
815
|
+
systemRole: 'Original role',
|
816
|
+
});
|
817
|
+
|
818
|
+
await trx.insert(agentsToSessions).values({
|
819
|
+
sessionId,
|
820
|
+
agentId,
|
821
|
+
userId,
|
822
|
+
});
|
823
|
+
});
|
824
|
+
|
825
|
+
// Update only some fields
|
826
|
+
await sessionModel.updateConfig(sessionId, {
|
827
|
+
model: 'gpt-4',
|
828
|
+
title: 'Updated Title',
|
829
|
+
// Don't update description and systemRole
|
830
|
+
});
|
831
|
+
|
832
|
+
// Verify merge behavior - updated fields changed, others preserved
|
833
|
+
const updatedAgent = await serverDB
|
834
|
+
.select()
|
835
|
+
.from(agents)
|
836
|
+
.where(and(eq(agents.id, agentId), eq(agents.userId, userId)));
|
837
|
+
|
838
|
+
expect(updatedAgent[0]).toMatchObject({
|
839
|
+
model: 'gpt-4',
|
840
|
+
title: 'Updated Title',
|
841
|
+
description: 'Original description', // Should be preserved
|
842
|
+
systemRole: 'Original role', // Should be preserved
|
843
|
+
});
|
844
|
+
});
|
845
|
+
|
846
|
+
it('should return early if session does not exist', async () => {
|
847
|
+
// Try to update config for non-existent session
|
848
|
+
const result = await sessionModel.updateConfig('non-existent-session', {
|
849
|
+
model: 'gpt-4',
|
850
|
+
title: 'Updated Title',
|
851
|
+
});
|
852
|
+
|
853
|
+
// Should return undefined/early without throwing
|
854
|
+
expect(result).toBeUndefined();
|
855
|
+
});
|
856
|
+
|
857
|
+
it('should throw error if session has no associated agent', async () => {
|
858
|
+
// Create session without agent
|
859
|
+
const sessionId = 'session-no-agent';
|
860
|
+
|
861
|
+
await serverDB.insert(sessions).values({
|
862
|
+
id: sessionId,
|
863
|
+
userId,
|
864
|
+
type: 'agent',
|
865
|
+
});
|
866
|
+
|
867
|
+
// Try to update config - should throw error
|
868
|
+
await expect(
|
869
|
+
sessionModel.updateConfig(sessionId, {
|
870
|
+
model: 'gpt-4',
|
871
|
+
title: 'Updated Title',
|
872
|
+
}),
|
873
|
+
).rejects.toThrow(
|
874
|
+
'this session is not assign with agent, please contact with admin to fix this issue.',
|
875
|
+
);
|
876
|
+
});
|
877
|
+
|
878
|
+
it('should return early if data is null or undefined', async () => {
|
879
|
+
// Create test session with agent
|
880
|
+
const sessionId = 'test-session-null';
|
881
|
+
const agentId = 'test-agent-null';
|
882
|
+
|
883
|
+
await serverDB.transaction(async (trx) => {
|
884
|
+
await trx.insert(sessions).values({
|
885
|
+
id: sessionId,
|
886
|
+
userId,
|
887
|
+
type: 'agent',
|
888
|
+
});
|
889
|
+
|
890
|
+
await trx.insert(agents).values({
|
891
|
+
id: agentId,
|
892
|
+
userId,
|
893
|
+
model: 'gpt-3.5-turbo',
|
894
|
+
title: 'Original Title',
|
895
|
+
});
|
896
|
+
|
897
|
+
await trx.insert(agentsToSessions).values({
|
898
|
+
sessionId,
|
899
|
+
agentId,
|
900
|
+
userId,
|
901
|
+
});
|
902
|
+
});
|
903
|
+
|
904
|
+
// Test with null data
|
905
|
+
const result1 = await sessionModel.updateConfig(sessionId, null);
|
906
|
+
expect(result1).toBeUndefined();
|
907
|
+
|
908
|
+
// Test with undefined data
|
909
|
+
const result2 = await sessionModel.updateConfig(sessionId, undefined);
|
910
|
+
expect(result2).toBeUndefined();
|
911
|
+
|
912
|
+
// Test with empty object
|
913
|
+
const result3 = await sessionModel.updateConfig(sessionId, {});
|
914
|
+
expect(result3).toBeUndefined();
|
915
|
+
});
|
916
|
+
|
917
|
+
it('should not update config for other users sessions', async () => {
|
781
918
|
// Create agent for another user
|
919
|
+
const sessionId = 'other-session';
|
782
920
|
const agentId = 'other-agent';
|
783
921
|
await serverDB.insert(users).values([{ id: 'other-user' }]);
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
922
|
+
|
923
|
+
await serverDB.transaction(async (trx) => {
|
924
|
+
await trx.insert(sessions).values({
|
925
|
+
id: sessionId,
|
926
|
+
userId: 'other-user',
|
927
|
+
type: 'agent',
|
928
|
+
});
|
929
|
+
|
930
|
+
await trx.insert(agents).values({
|
931
|
+
id: agentId,
|
932
|
+
userId: 'other-user',
|
933
|
+
model: 'gpt-3.5-turbo',
|
934
|
+
title: 'Original Title',
|
935
|
+
});
|
936
|
+
|
937
|
+
await trx.insert(agentsToSessions).values({
|
938
|
+
sessionId,
|
939
|
+
agentId,
|
940
|
+
userId: 'other-user',
|
941
|
+
});
|
789
942
|
});
|
790
943
|
|
791
|
-
// Try to update other user's
|
792
|
-
await sessionModel.updateConfig(
|
944
|
+
// Try to update other user's session - should return early
|
945
|
+
const result = await sessionModel.updateConfig(sessionId, {
|
793
946
|
model: 'gpt-4',
|
794
947
|
title: 'Updated Title',
|
795
948
|
});
|
796
949
|
|
950
|
+
// Should return undefined as session doesn't belong to current user
|
951
|
+
expect(result).toBeUndefined();
|
952
|
+
|
797
953
|
// Verify no changes were made
|
798
954
|
const agent = await serverDB.select().from(agents).where(eq(agents.id, agentId));
|
799
955
|
|
@@ -393,13 +393,23 @@ export class SessionModel {
|
|
393
393
|
.returning();
|
394
394
|
};
|
395
395
|
|
396
|
-
updateConfig = async (
|
397
|
-
if (Object.keys(data).length === 0) return;
|
396
|
+
updateConfig = async (sessionId: string, data: DeepPartial<AgentItem> | undefined | null) => {
|
397
|
+
if (!data || Object.keys(data).length === 0) return;
|
398
398
|
|
399
|
+
const session = await this.findByIdOrSlug(sessionId);
|
400
|
+
if (!session) return;
|
401
|
+
|
402
|
+
if (!session.agent) {
|
403
|
+
throw new Error(
|
404
|
+
'this session is not assign with agent, please contact with admin to fix this issue.',
|
405
|
+
);
|
406
|
+
}
|
407
|
+
|
408
|
+
const mergedValue = merge(session.agent, data);
|
399
409
|
return this.db
|
400
410
|
.update(agents)
|
401
|
-
.set(
|
402
|
-
.where(and(eq(agents.id, id), eq(agents.userId, this.userId)));
|
411
|
+
.set(mergedValue)
|
412
|
+
.where(and(eq(agents.id, session.agent.id), eq(agents.userId, this.userId)));
|
403
413
|
};
|
404
414
|
|
405
415
|
// **************** Helper *************** //
|
@@ -82,6 +82,10 @@ interface LobeGoogleAIParams {
|
|
82
82
|
isVertexAi?: boolean;
|
83
83
|
}
|
84
84
|
|
85
|
+
interface GoogleAIThinkingConfig {
|
86
|
+
includeThoughts?: boolean;
|
87
|
+
}
|
88
|
+
|
85
89
|
export class LobeGoogleAI implements LobeRuntimeAI {
|
86
90
|
private client: GoogleGenerativeAI;
|
87
91
|
private isVertexAi: boolean;
|
@@ -106,6 +110,10 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
106
110
|
const payload = this.buildPayload(rawPayload);
|
107
111
|
const model = payload.model;
|
108
112
|
|
113
|
+
const thinkingConfig: GoogleAIThinkingConfig = {
|
114
|
+
includeThoughts: true,
|
115
|
+
};
|
116
|
+
|
109
117
|
const contents = await this.buildGoogleMessages(payload.messages);
|
110
118
|
|
111
119
|
const inputStartAt = Date.now();
|
@@ -117,6 +125,7 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
117
125
|
// @ts-expect-error - Google SDK 0.24.0 doesn't have this property for now with
|
118
126
|
response_modalities: modelsWithModalities.has(model) ? ['Text', 'Image'] : undefined,
|
119
127
|
temperature: payload.temperature,
|
128
|
+
thinkingConfig,
|
120
129
|
topP: payload.top_p,
|
121
130
|
},
|
122
131
|
model,
|
@@ -76,6 +76,15 @@ const transformGoogleGenerativeAIStream = (
|
|
76
76
|
const text = chunk.text?.();
|
77
77
|
|
78
78
|
if (candidate) {
|
79
|
+
// 首先检查是否为 reasoning 内容 (thought: true)
|
80
|
+
if (Array.isArray(candidate.content.parts) && candidate.content.parts.length > 0) {
|
81
|
+
for (const part of candidate.content.parts) {
|
82
|
+
if (part && part.text && (part as any).thought === true) {
|
83
|
+
return { data: part.text, id: context.id, type: 'reasoning' };
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
79
88
|
// return the grounding
|
80
89
|
if (candidate.groundingMetadata) {
|
81
90
|
const { webSearchQueries, groundingChunks } = candidate.groundingMetadata;
|
@@ -10,7 +10,6 @@ import { AgentChatConfigSchema } from '@/types/agent';
|
|
10
10
|
import { LobeMetaDataSchema } from '@/types/meta';
|
11
11
|
import { BatchTaskResult } from '@/types/service';
|
12
12
|
import { ChatSessionList } from '@/types/session';
|
13
|
-
import { merge } from '@/utils/merge';
|
14
13
|
|
15
14
|
const sessionProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
|
16
15
|
const { ctx } = opts;
|
@@ -156,12 +155,8 @@ export const sessionRouter = router({
|
|
156
155
|
}),
|
157
156
|
)
|
158
157
|
.mutation(async ({ input, ctx }) => {
|
159
|
-
|
160
|
-
|
161
|
-
if (!session) return;
|
162
|
-
|
163
|
-
return ctx.sessionModel.updateConfig(session.agent.id, {
|
164
|
-
chatConfig: merge(session.agent.chatConfig, input.value),
|
158
|
+
return ctx.sessionModel.updateConfig(input.id, {
|
159
|
+
chatConfig: input.value,
|
165
160
|
});
|
166
161
|
}),
|
167
162
|
updateSessionConfig: sessionProcedure
|
@@ -172,18 +167,7 @@ export const sessionRouter = router({
|
|
172
167
|
}),
|
173
168
|
)
|
174
169
|
.mutation(async ({ input, ctx }) => {
|
175
|
-
|
176
|
-
|
177
|
-
if (!session || !input.value) return;
|
178
|
-
|
179
|
-
if (!session.agent) {
|
180
|
-
throw new Error(
|
181
|
-
'this session is not assign with agent, please contact with admin to fix this issue.',
|
182
|
-
);
|
183
|
-
}
|
184
|
-
|
185
|
-
const mergedValue = merge(session.agent, input.value);
|
186
|
-
return ctx.sessionModel.updateConfig(session.agent.id, mergedValue);
|
170
|
+
return ctx.sessionModel.updateConfig(input.id, input.value);
|
187
171
|
}),
|
188
172
|
});
|
189
173
|
|
@@ -10,6 +10,9 @@ vi.mock('@/config/app', () => ({
|
|
10
10
|
appEnv: {
|
11
11
|
DEFAULT_AGENT_CONFIG: 'model=gpt-4;temperature=0.7',
|
12
12
|
},
|
13
|
+
getAppConfig: () => ({
|
14
|
+
DEFAULT_AGENT_CONFIG: 'model=gpt-4;temperature=0.7',
|
15
|
+
}),
|
13
16
|
}));
|
14
17
|
|
15
18
|
vi.mock('@/server/globalConfig/parseDefaultAgent', () => ({
|
@@ -1,7 +1,6 @@
|
|
1
|
-
import { appEnv } from '@/config/app';
|
2
1
|
import { SessionModel } from '@/database/models/session';
|
3
2
|
import { LobeChatDatabase } from '@/database/type';
|
4
|
-
import {
|
3
|
+
import { getServerDefaultAgentConfig } from '@/server/globalConfig';
|
5
4
|
|
6
5
|
export class AgentService {
|
7
6
|
private readonly userId: string;
|
@@ -14,9 +13,7 @@ export class AgentService {
|
|
14
13
|
|
15
14
|
async createInbox() {
|
16
15
|
const sessionModel = new SessionModel(this.db, this.userId);
|
17
|
-
|
18
|
-
const defaultAgentConfig = parseAgentConfig(appEnv.DEFAULT_AGENT_CONFIG) || {};
|
19
|
-
|
16
|
+
const defaultAgentConfig = getServerDefaultAgentConfig();
|
20
17
|
await sessionModel.createInbox(defaultAgentConfig);
|
21
18
|
}
|
22
19
|
}
|
@@ -106,10 +106,7 @@ export class ClientService extends BaseClientService implements ISessionService
|
|
106
106
|
};
|
107
107
|
|
108
108
|
updateSessionConfig: ISessionService['updateSessionConfig'] = async (activeId, config) => {
|
109
|
-
|
110
|
-
if (!session || !config) return;
|
111
|
-
|
112
|
-
return this.sessionModel.updateConfig(session.agent.id, config as AgentItem);
|
109
|
+
return this.sessionModel.updateConfig(activeId, config as AgentItem);
|
113
110
|
};
|
114
111
|
|
115
112
|
updateSessionMeta: ISessionService['updateSessionMeta'] = async (activeId, meta) => {
|
@@ -11,6 +11,7 @@ vi.mock('i18next', () => ({
|
|
11
11
|
|
12
12
|
// 定义一个变量来存储 enableAuth 的值
|
13
13
|
let enableAuth = true;
|
14
|
+
let isDesktop = false;
|
14
15
|
|
15
16
|
// 模拟 @/const/auth 模块
|
16
17
|
vi.mock('@/const/auth', () => ({
|
@@ -19,14 +20,23 @@ vi.mock('@/const/auth', () => ({
|
|
19
20
|
},
|
20
21
|
}));
|
21
22
|
|
23
|
+
// 模拟 @/const/version 模块
|
24
|
+
vi.mock('@/const/version', () => ({
|
25
|
+
get isDesktop() {
|
26
|
+
return isDesktop;
|
27
|
+
},
|
28
|
+
}));
|
29
|
+
|
22
30
|
afterEach(() => {
|
23
31
|
enableAuth = true;
|
32
|
+
isDesktop = false;
|
24
33
|
});
|
25
34
|
|
26
35
|
describe('userProfileSelectors', () => {
|
27
36
|
describe('nickName', () => {
|
28
|
-
it('should return default nickname when auth is disabled', () => {
|
37
|
+
it('should return default nickname when auth is disabled and not desktop', () => {
|
29
38
|
enableAuth = false;
|
39
|
+
isDesktop = false;
|
30
40
|
|
31
41
|
const store: UserStore = {
|
32
42
|
isSignedIn: false,
|
@@ -38,6 +48,19 @@ describe('userProfileSelectors', () => {
|
|
38
48
|
expect(t).toHaveBeenCalledWith('userPanel.defaultNickname', { ns: 'common' });
|
39
49
|
});
|
40
50
|
|
51
|
+
it('should return user fullName when auth is disabled and is desktop', () => {
|
52
|
+
enableAuth = false;
|
53
|
+
isDesktop = true;
|
54
|
+
|
55
|
+
const store: UserStore = {
|
56
|
+
isSignedIn: false,
|
57
|
+
user: { fullName: 'John Doe' },
|
58
|
+
enableAuth: () => false,
|
59
|
+
} as unknown as UserStore;
|
60
|
+
|
61
|
+
expect(userProfileSelectors.nickName(store)).toBe('John Doe');
|
62
|
+
});
|
63
|
+
|
41
64
|
it('should return user fullName when signed in', () => {
|
42
65
|
enableAuth = true;
|
43
66
|
|
@@ -75,8 +98,9 @@ describe('userProfileSelectors', () => {
|
|
75
98
|
});
|
76
99
|
|
77
100
|
describe('username', () => {
|
78
|
-
it('should return default username when auth is disabled', () => {
|
101
|
+
it('should return default username when auth is disabled and not desktop', () => {
|
79
102
|
enableAuth = false;
|
103
|
+
isDesktop = false;
|
80
104
|
|
81
105
|
const store: UserStore = {
|
82
106
|
isSignedIn: false,
|
@@ -87,6 +111,19 @@ describe('userProfileSelectors', () => {
|
|
87
111
|
expect(userProfileSelectors.username(store)).toBe('LobeChat');
|
88
112
|
});
|
89
113
|
|
114
|
+
it('should return user username when auth is disabled and is desktop', () => {
|
115
|
+
enableAuth = false;
|
116
|
+
isDesktop = true;
|
117
|
+
|
118
|
+
const store: UserStore = {
|
119
|
+
isSignedIn: false,
|
120
|
+
user: { username: 'johndoe' },
|
121
|
+
enableAuth: () => false,
|
122
|
+
} as unknown as UserStore;
|
123
|
+
|
124
|
+
expect(userProfileSelectors.username(store)).toBe('johndoe');
|
125
|
+
});
|
126
|
+
|
90
127
|
it('should return user username when signed in', () => {
|
91
128
|
const store: UserStore = {
|
92
129
|
isSignedIn: true,
|
@@ -4,19 +4,31 @@ import { enableAuth, enableClerk, enableNextAuth } from '@/const/auth';
|
|
4
4
|
import { BRANDING_NAME } from '@/const/branding';
|
5
5
|
import { UserStore } from '@/store/user';
|
6
6
|
import { LobeUser } from '@/types/user';
|
7
|
+
import { isDesktop } from '@/const/version';
|
7
8
|
|
8
9
|
const DEFAULT_USERNAME = BRANDING_NAME;
|
9
10
|
|
10
11
|
const nickName = (s: UserStore) => {
|
11
|
-
|
12
|
+
const defaultNickName = s.user?.fullName || s.user?.username;
|
13
|
+
if (!enableAuth) {
|
14
|
+
if (isDesktop) {
|
15
|
+
return defaultNickName;
|
16
|
+
}
|
17
|
+
return t('userPanel.defaultNickname', { ns: 'common' });
|
18
|
+
}
|
12
19
|
|
13
|
-
if (s.isSignedIn) return
|
20
|
+
if (s.isSignedIn) return defaultNickName;
|
14
21
|
|
15
22
|
return t('userPanel.anonymousNickName', { ns: 'common' });
|
16
23
|
};
|
17
24
|
|
18
25
|
const username = (s: UserStore) => {
|
19
|
-
if (!enableAuth)
|
26
|
+
if (!enableAuth) {
|
27
|
+
if (isDesktop) {
|
28
|
+
return s.user?.username;
|
29
|
+
}
|
30
|
+
return DEFAULT_USERNAME;
|
31
|
+
}
|
20
32
|
|
21
33
|
if (s.isSignedIn) return s.user?.username;
|
22
34
|
|
@@ -83,6 +83,7 @@ describe('createCommonSlice', () => {
|
|
83
83
|
settings: {
|
84
84
|
general: { fontSize: 14 },
|
85
85
|
},
|
86
|
+
email: 'test@example.com'
|
86
87
|
};
|
87
88
|
|
88
89
|
vi.spyOn(userService, 'getUserState').mockResolvedValueOnce(mockUserState);
|
@@ -104,6 +105,7 @@ describe('createCommonSlice', () => {
|
|
104
105
|
// 验证状态是否正确更新
|
105
106
|
expect(useUserStore.getState().user?.avatar).toBe(mockUserState.avatar);
|
106
107
|
expect(useUserStore.getState().settings).toEqual(mockUserState.settings);
|
108
|
+
expect(useUserStore.getState().user?.email).toEqual(mockUserState.email);
|
107
109
|
expect(successCallback).toHaveBeenCalledWith(mockUserState);
|
108
110
|
});
|
109
111
|
|