@lobehub/chat 1.87.9 → 1.88.0

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 CHANGED
@@ -2,6 +2,40 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 1.88.0](https://github.com/lobehub/lobe-chat/compare/v1.87.9...v1.88.0)
6
+
7
+ <sup>Released on **2025-05-23**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: Add claude 4 series.
12
+
13
+ #### 🐛 Bug Fixes
14
+
15
+ - **misc**: Fix missing email field to user, update agent config of client db will override old config.
16
+
17
+ <br/>
18
+
19
+ <details>
20
+ <summary><kbd>Improvements and Fixes</kbd></summary>
21
+
22
+ #### What's improved
23
+
24
+ - **misc**: Add claude 4 series, closes [#7939](https://github.com/lobehub/lobe-chat/issues/7939) ([9b4f950](https://github.com/lobehub/lobe-chat/commit/9b4f950))
25
+
26
+ #### What's fixed
27
+
28
+ - **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))
29
+ - **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))
30
+
31
+ </details>
32
+
33
+ <div align="right">
34
+
35
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
36
+
37
+ </div>
38
+
5
39
  ### [Version 1.87.9](https://github.com/lobehub/lobe-chat/compare/v1.87.8...v1.87.9)
6
40
 
7
41
  <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 | Description |
331
- | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
332
- | [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2025-03-23**</sup> | Analyze stocks and get comprehensive real-time investment data and analytics.<br/>`stock` |
333
- | [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` |
334
- | [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` |
335
- | [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` |
336
-
337
- > 📊 Total plugins: [<kbd>**43**</kbd>](https://lobechat.com/discover/plugins)
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
- | [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2025-03-23**</sup> | 分析股票并获取全面的实时投资数据和分析。<br/>`股票` |
326
- | [网页](https://lobechat.com/discover/plugin/web)<br/><sup>By **Proghit** on **2025-01-24**</sup> | 智能网页搜索,读取和分析页面,以提供来自 Google 结果的全面答案。<br/>`网页` `搜索` |
327
- | [必应网页搜索](https://lobechat.com/discover/plugin/Bingsearch-identifier)<br/><sup>By **FineHow** on **2024-12-22**</sup> | 通过 BingApi 搜索互联网上的信息<br/>`bingsearch` |
328
- | [谷歌自定义搜索引擎](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | 通过他们的官方自定义搜索引擎 API 搜索谷歌。<br/>`网络` `搜索` |
329
-
330
- > 📊 Total plugins: [<kbd>**43**</kbd>](https://lobechat.com/discover/plugins)
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,16 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "features": [
5
+ "Add claude 4 series."
6
+ ],
7
+ "fixes": [
8
+ "Fix missing email field to user, update agent config of client db will override old config."
9
+ ]
10
+ },
11
+ "date": "2025-05-23",
12
+ "version": "1.88.0"
13
+ },
2
14
  {
3
15
  "children": {
4
16
  "improvements": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.87.9",
3
+ "version": "1.88.0",
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: {
@@ -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
- await serverDB.insert(agents).values({
754
- id: agentId,
755
- userId,
756
- model: 'gpt-3.5-turbo',
757
- title: 'Original Title',
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(agentId, {
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 not update config for other users agents', async () => {
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
- await serverDB.insert(agents).values({
785
- id: agentId,
786
- userId: 'other-user',
787
- model: 'gpt-3.5-turbo',
788
- title: 'Original Title',
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 agent
792
- await sessionModel.updateConfig(agentId, {
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 (id: string, data: Partial<AgentItem>) => {
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(data)
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 *************** //
@@ -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
- const session = await ctx.sessionModel.findByIdOrSlug(input.id);
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
- const session = await ctx.sessionModel.findByIdOrSlug(input.id);
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 { parseAgentConfig } from '@/server/globalConfig/parseDefaultAgent';
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
- const session = await this.sessionModel.findByIdOrSlug(activeId);
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) => {
@@ -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
 
@@ -93,6 +93,7 @@ export const createCommonSlice: StateCreator<
93
93
  data.avatar || data.userId
94
94
  ? merge(get().user, {
95
95
  avatar: data.avatar,
96
+ email: data.email,
96
97
  firstName: data.firstName,
97
98
  fullName: data.fullName,
98
99
  id: data.userId,