@lobehub/chat 0.42.3 → 0.44.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.
Files changed (74) hide show
  1. package/.github/workflows/release.yml +1 -1
  2. package/CHANGELOG.md +74 -0
  3. package/package.json +5 -2
  4. package/src/const/meta.ts +3 -0
  5. package/src/const/session.ts +1 -0
  6. package/src/helpers/export.ts +15 -9
  7. package/src/locales/default/common.ts +8 -1
  8. package/src/pages/chat/SessionList/Inbox.tsx +23 -0
  9. package/src/pages/chat/SessionList/List/SessionItem.tsx +15 -4
  10. package/src/pages/chat/SessionList/List/index.tsx +3 -2
  11. package/src/pages/chat/SessionList/index.tsx +13 -1
  12. package/src/pages/chat/[id]/index.page.tsx +7 -9
  13. package/src/pages/chat/[id]/layout.tsx +7 -23
  14. package/src/pages/chat/[id]/setting/index.page.tsx +3 -3
  15. package/src/pages/chat/features/Conversation/ChatList/SkeletonList.tsx +43 -0
  16. package/src/pages/chat/{[id] → features}/Conversation/ChatList/index.tsx +35 -5
  17. package/src/pages/chat/{[id] → features}/Conversation/Input/index.tsx +1 -1
  18. package/src/pages/chat/{[id] → features}/Header/index.tsx +32 -34
  19. package/src/pages/chat/features/Sidebar/Inner.tsx +42 -0
  20. package/src/pages/chat/{[id]/Sidebar/Inner.tsx → features/Sidebar/SystemRole.tsx} +7 -37
  21. package/src/pages/chat/{[id] → features}/Sidebar/index.tsx +8 -4
  22. package/src/pages/chat/index.page.tsx +39 -1
  23. package/src/pages/chat/layout.tsx +27 -0
  24. package/src/pages/settings/Settings/Common.tsx +11 -2
  25. package/src/pages/welcome/Footer/index.tsx +1 -0
  26. package/src/services/chatModel.ts +1 -1
  27. package/src/store/global/hooks/useOnFinishHydrationGlobal.ts +18 -0
  28. package/src/store/global/hooks/useSwitchSideBarOnInit.ts +4 -8
  29. package/src/store/global/slices/settings.ts +2 -2
  30. package/src/store/session/hooks/index.ts +2 -0
  31. package/src/store/session/hooks/useOnFinishHydrationSession.ts +18 -0
  32. package/src/store/session/hooks/useSessionHydrated.ts +1 -1
  33. package/src/store/session/index.ts +1 -3
  34. package/src/store/session/initialState.ts +6 -3
  35. package/src/store/session/selectors.ts +3 -3
  36. package/src/store/session/slices/agentConfig/action.ts +2 -1
  37. package/src/store/session/slices/agentConfig/selectors.ts +11 -11
  38. package/src/store/session/slices/chat/actions/message.ts +13 -20
  39. package/src/store/session/slices/chat/actions/topic.ts +3 -1
  40. package/src/store/session/slices/chat/selectors/chat.ts +1 -1
  41. package/src/store/session/slices/chat/selectors/token.ts +1 -1
  42. package/src/store/session/slices/chat/selectors/topic.ts +4 -2
  43. package/src/store/session/slices/session/action.ts +29 -15
  44. package/src/store/session/slices/session/initialState.ts +18 -4
  45. package/src/store/session/slices/session/selectors/export.ts +13 -8
  46. package/src/store/session/slices/session/selectors/list.test.ts +303 -0
  47. package/src/store/session/slices/session/selectors/list.ts +8 -3
  48. package/src/store/session/store.ts +5 -5
  49. package/src/types/exportConfig.ts +93 -31
  50. package/src/utils/config.ts +9 -0
  51. package/src/pages/chat/SessionList/List/style.ts +0 -36
  52. package/src/store/session/slices/chat/index.ts +0 -3
  53. package/src/store/session/slices/session/index.ts +0 -3
  54. package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/ApiKeyForm.tsx +0 -0
  55. package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/ErrorActionContainer.tsx +0 -0
  56. package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/InvalidAccess.tsx +1 -1
  57. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/OpenAiBizError.tsx +0 -0
  58. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/style.tsx +0 -0
  59. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/MessageExtra.tsx +0 -0
  60. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/OTPInput.tsx +0 -0
  61. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Plugins/FunctionCall.tsx +0 -0
  62. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Plugins/PluginMessage.tsx +0 -0
  63. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Plugins/PluginResultRender.tsx +0 -0
  64. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Plugins/style.ts +0 -0
  65. /package/src/pages/chat/{[id] → features}/Conversation/Input/Action.tsx +0 -0
  66. /package/src/pages/chat/{[id] → features}/Conversation/Input/ActionRight.tsx +0 -0
  67. /package/src/pages/chat/{[id] → features}/Conversation/Input/Token.tsx +0 -0
  68. /package/src/pages/chat/{[id] → features}/Conversation/index.tsx +0 -0
  69. /package/src/pages/chat/{[id] → features}/Header/PluginTag.tsx +0 -0
  70. /package/src/pages/chat/{[id] → features}/Sidebar/Header.tsx +0 -0
  71. /package/src/pages/chat/{[id] → features}/Sidebar/Topic/DefaultContent.tsx +0 -0
  72. /package/src/pages/chat/{[id] → features}/Sidebar/Topic/TopicContent.tsx +0 -0
  73. /package/src/pages/chat/{[id] → features}/Sidebar/Topic/TopicItem.tsx +0 -0
  74. /package/src/pages/chat/{[id] → features}/Sidebar/Topic/index.tsx +0 -0
@@ -2,12 +2,14 @@ import { StateCreator } from 'zustand/vanilla';
2
2
 
3
3
  import { LOADING_FLAT } from '@/const/message';
4
4
  import { promptSummaryTitle } from '@/prompts/chat';
5
- import { SessionStore, chatSelectors, sessionSelectors, topicSelectors } from '@/store/session';
5
+ import { SessionStore } from '@/store/session';
6
6
  import { fetchPresetTaskResult } from '@/utils/fetch';
7
7
  import { setNamespace } from '@/utils/storeDebug';
8
8
  import { nanoid } from '@/utils/uuid';
9
9
 
10
+ import { sessionSelectors } from '../../session/selectors';
10
11
  import { ChatTopicDispatch, topicReducer } from '../reducers/topic';
12
+ import { chatSelectors, topicSelectors } from '../selectors';
11
13
 
12
14
  const t = setNamespace('chat/topic');
13
15
  export interface ChatTopicAction {
@@ -4,7 +4,7 @@ import { ChatMessage } from '@/types/chatMessage';
4
4
 
5
5
  import type { SessionStore } from '../../../store';
6
6
  import { agentSelectors } from '../../agentConfig';
7
- import { sessionSelectors } from '../../session';
7
+ import { sessionSelectors } from '../../session/selectors';
8
8
  import { getSlicedMessagesWithConfig } from '../utils';
9
9
  import { organizeChats } from './utils';
10
10
 
@@ -1,6 +1,6 @@
1
1
  import { encode } from 'gpt-tokenizer';
2
2
 
3
- import { agentSelectors } from '@/store/session';
3
+ import { agentSelectors } from '@/store/session/slices/agentConfig';
4
4
 
5
5
  import type { SessionStore } from '../../../store';
6
6
  import { currentChatsWithHistoryConfig } from './chat';
@@ -1,8 +1,10 @@
1
1
  // 展示在聊天框中的消息
2
- import { SessionStore, sessionSelectors } from '@/store/session';
3
- import { organizeChats } from '@/store/session/slices/chat/selectors/utils';
4
2
  import { ChatTopic } from '@/types/topic';
5
3
 
4
+ import { SessionStore } from '../../../store';
5
+ import { sessionSelectors } from '../../session/selectors';
6
+ import { organizeChats } from './utils';
7
+
6
8
  export const currentTopics = (s: SessionStore): ChatTopic[] => {
7
9
  const session = sessionSelectors.currentSession(s);
8
10
  if (!session) return [];
@@ -3,19 +3,21 @@ import { merge } from 'lodash-es';
3
3
  import Router from 'next/router';
4
4
  import { StateCreator } from 'zustand/vanilla';
5
5
 
6
+ import { INBOX_SESSION_ID } from '@/const/session';
6
7
  import { useGlobalStore } from '@/store/global';
7
- import { SessionStore, initLobeSession } from '@/store/session';
8
+ import { SessionStore } from '@/store/session';
8
9
  import { LobeAgentSession, LobeSessions } from '@/types/session';
9
10
  import { setNamespace } from '@/utils/storeDebug';
10
11
  import { uuid } from '@/utils/uuid';
11
12
 
13
+ import { initLobeSession } from './initialState';
12
14
  import { SessionDispatch, sessionsReducer } from './reducers/session';
13
15
 
14
16
  const t = setNamespace('session');
17
+
15
18
  export interface SessionAction {
16
19
  activeSession: (sessionId: string) => void;
17
20
  clearSessions: () => void;
18
-
19
21
  /**
20
22
  * @title 添加会话
21
23
  * @param session - 会话信息
@@ -27,7 +29,6 @@ export interface SessionAction {
27
29
  * @param payload - 聊天记录
28
30
  */
29
31
  dispatchSession: (payload: SessionDispatch) => void;
30
-
31
32
  /**
32
33
  * 导入会话
33
34
  * @param sessions
@@ -39,6 +40,7 @@ export interface SessionAction {
39
40
  * @param sessionId
40
41
  */
41
42
  pinSession: (sessionId: string, pinned?: boolean) => void;
43
+
42
44
  /**
43
45
  * 生成压缩后的消息
44
46
  * @returns 压缩后的消息
@@ -50,6 +52,7 @@ export interface SessionAction {
50
52
  * @returns void
51
53
  */
52
54
  removeSession: (sessionId: string) => void;
55
+ switchInbox: () => void;
53
56
 
54
57
  /**
55
58
  * @title 切换会话
@@ -95,11 +98,21 @@ export const createSessionSlice: StateCreator<
95
98
 
96
99
  dispatchSession: (payload) => {
97
100
  const { type, ...res } = payload;
98
- set(
99
- { sessions: sessionsReducer(get().sessions, payload) },
100
- false,
101
- t(`dispatchChat/${type}`, res),
102
- );
101
+
102
+ // 如果是 inbox 类型的 session
103
+ if ('id' in res && res.id === INBOX_SESSION_ID) {
104
+ const nextInbox = sessionsReducer({ inbox: get().inbox }, payload) as {
105
+ inbox: LobeAgentSession;
106
+ };
107
+ set({ inbox: nextInbox.inbox }, false, t(`dispatchInbox/${type}`, res));
108
+ } else {
109
+ // 常规类型的session
110
+ set(
111
+ { sessions: sessionsReducer(get().sessions, payload) },
112
+ false,
113
+ t(`dispatchSessions/${type}`, res),
114
+ );
115
+ }
103
116
  },
104
117
 
105
118
  importSessions: (importSessions) => {
@@ -119,13 +132,7 @@ export const createSessionSlice: StateCreator<
119
132
  t('importSessions', importSessions),
120
133
  );
121
134
  },
122
- // genShareUrl: () => {
123
- // const session = sessionSelectors.currentSession(get());
124
- // if (!session) return '';
125
- //
126
- // const agent = session.config;
127
- // return genShareMessagesUrl(session.chats, agent.systemRole);
128
- // },
135
+
129
136
  pinSession: (sessionId, pinned) => {
130
137
  const nextValue = typeof pinned === 'boolean' ? pinned : !get().sessions[sessionId].pinned;
131
138
 
@@ -140,6 +147,13 @@ export const createSessionSlice: StateCreator<
140
147
  }
141
148
  },
142
149
 
150
+ switchInbox: () => {
151
+ if (get().activeId === INBOX_SESSION_ID) return;
152
+ get().activeSession(INBOX_SESSION_ID);
153
+
154
+ Router.push('/chat');
155
+ },
156
+
143
157
  switchSession: async (sessionId) => {
144
158
  if (get().activeId === sessionId) return;
145
159
 
@@ -1,7 +1,9 @@
1
- import { DEFAULT_AGENT_META } from '@/const/meta';
2
- import { LobeAgentSession, LobeSessionType } from '@/types/session';
1
+ import { merge } from 'lodash-es';
3
2
 
4
- import { initialLobeAgentConfig } from '../agentConfig';
3
+ import { DEFAULT_AGENT_META, DEFAULT_INBOX_AVATAR } from '@/const/meta';
4
+ import { LobeAgentConfig, LobeAgentSession, LobeSessionType } from '@/types/session';
5
+
6
+ import { initialLobeAgentConfig } from '../agentConfig/initialState';
5
7
 
6
8
  export interface SessionState {
7
9
  /**
@@ -10,6 +12,8 @@ export interface SessionState {
10
12
  * @default null
11
13
  */
12
14
  activeId: string | null;
15
+ // 默认会话
16
+ inbox: LobeAgentSession;
13
17
  searchKeywords: string;
14
18
  sessions: Record<string, LobeAgentSession>;
15
19
  }
@@ -24,9 +28,19 @@ export const initLobeSession: LobeAgentSession = {
24
28
  updateAt: Date.now(),
25
29
  };
26
30
 
31
+ export const initInbox = merge({}, initLobeSession, {
32
+ config: {
33
+ systemRole: '你是一名 AI 助理',
34
+ } as LobeAgentConfig,
35
+ id: 'inbox',
36
+ meta: {
37
+ avatar: DEFAULT_INBOX_AVATAR,
38
+ },
39
+ } as Partial<LobeAgentSession>);
40
+
27
41
  export const initialSessionState: SessionState = {
28
42
  activeId: null,
29
-
43
+ inbox: initInbox,
30
44
  searchKeywords: '',
31
45
  sessions: {},
32
46
  };
@@ -1,20 +1,25 @@
1
1
  import { transform } from 'lodash-es';
2
2
 
3
3
  import { SessionStore } from '@/store/session';
4
+ import { ConfigStateAgents, ConfigStateSessions } from '@/types/exportConfig';
4
5
  import { LobeAgentSession, LobeSessions } from '@/types/session';
5
6
 
6
7
  import { getSessionById } from './list';
7
8
 
8
- export const exportSessions = (s: SessionStore) => s.sessions;
9
+ export const exportSessions = (s: SessionStore): ConfigStateSessions => ({
10
+ inbox: s.inbox,
11
+ sessions: s.sessions,
12
+ });
9
13
 
10
- // 排除 chats
11
- export const exportAgents = (s: SessionStore) => {
12
- return transform(s.sessions, (result: LobeSessions, value, key) => {
13
- // 移除 chats 和 topics
14
- result[key] = { ...value, chats: {}, topics: {} } as LobeAgentSession;
15
- });
14
+ export const exportAgents = (s: SessionStore): ConfigStateAgents => {
15
+ return {
16
+ sessions: transform(s.sessions, (result: LobeSessions, value, key) => {
17
+ // 移除 chats 和 topics
18
+ result[key] = { ...value, chats: {}, topics: {} } as LobeAgentSession;
19
+ }),
20
+ };
16
21
  };
17
- // 排除 chats
22
+
18
23
  export const getExportAgent =
19
24
  (id: string) =>
20
25
  (s: SessionStore): LobeAgentSession => {
@@ -0,0 +1,303 @@
1
+ import type { SessionStore } from '@/store/session';
2
+ import { LanguageModel } from '@/types/llm';
3
+ import { LobeAgentSession, LobeSessionType } from '@/types/session';
4
+
5
+ import { initLobeSession } from '../initialState';
6
+ import {
7
+ currentSession,
8
+ currentSessionSafe,
9
+ getSessionById,
10
+ getSessionMetaById,
11
+ hasSessionList,
12
+ sessionList,
13
+ } from './list';
14
+
15
+ describe('currentSession', () => {
16
+ const s = {
17
+ activeId: '1',
18
+ sessions: {
19
+ '1': {
20
+ id: '1',
21
+ config: {
22
+ model: LanguageModel.GPT3_5,
23
+ params: {},
24
+ systemRole: 'system-role',
25
+ },
26
+ type: LobeSessionType.Agent,
27
+ } as LobeAgentSession,
28
+ '2': {
29
+ id: '2',
30
+ config: {
31
+ model: LanguageModel.GPT3_5,
32
+ params: {},
33
+ systemRole: 'system-role',
34
+ },
35
+ type: LobeSessionType.Agent,
36
+ } as LobeAgentSession,
37
+ },
38
+ inbox: {
39
+ id: 'inbox',
40
+ config: {
41
+ model: LanguageModel.GPT3_5,
42
+ params: {},
43
+ systemRole: 'system-role',
44
+ },
45
+ type: LobeSessionType.Agent,
46
+ } as LobeAgentSession,
47
+ } as unknown as SessionStore;
48
+
49
+ it('should return undefined when s.activeId is not defined', () => {
50
+ expect(currentSession({ sessions: {} } as any)).toBeUndefined();
51
+ });
52
+
53
+ it('should return s.inbox when s.activeId is equal to INBOX_SESSION_ID', () => {
54
+ expect(currentSession({ ...s, activeId: 'inbox' })).toEqual(s.inbox);
55
+ });
56
+
57
+ it('should return s.sessions[s.activeId] when s.activeId is not equal to INBOX_SESSION_ID', () => {
58
+ expect(currentSession(s)).toEqual(s.sessions['1']);
59
+ });
60
+ });
61
+
62
+ describe('currentSessionSafe', () => {
63
+ const s = {
64
+ activeId: '1',
65
+ sessions: {
66
+ '1': {
67
+ id: '1',
68
+ config: {
69
+ model: LanguageModel.GPT3_5,
70
+ params: {},
71
+ systemRole: 'system-role',
72
+ },
73
+ type: LobeSessionType.Agent,
74
+ } as LobeAgentSession,
75
+ '2': {
76
+ id: '2',
77
+ config: {
78
+ model: LanguageModel.GPT3_5,
79
+ params: {},
80
+ systemRole: 'system-role',
81
+ },
82
+ type: LobeSessionType.Agent,
83
+ } as LobeAgentSession,
84
+ },
85
+ inbox: {
86
+ id: 'inbox',
87
+ config: {
88
+ model: LanguageModel.GPT3_5,
89
+ params: {},
90
+ systemRole: 'system-role',
91
+ },
92
+ type: LobeSessionType.Agent,
93
+ } as LobeAgentSession,
94
+ } as unknown as SessionStore;
95
+
96
+ it('should return initLobeSession when currentSession(s) returns undefined', () => {
97
+ expect(currentSessionSafe({ sessions: {} } as any)).toEqual(initLobeSession);
98
+ });
99
+
100
+ it('should return the result of currentSession(s) when it returns a non-undefined value', () => {
101
+ expect(currentSessionSafe(s)).toEqual(s.sessions['1']);
102
+ });
103
+ });
104
+
105
+ describe('sessionList', () => {
106
+ const s = {
107
+ sessions: {
108
+ '1': {
109
+ pinned: true,
110
+ updateAt: 100,
111
+ },
112
+ '2': {
113
+ pinned: false,
114
+ updateAt: 200,
115
+ },
116
+ '3': {
117
+ pinned: true,
118
+ updateAt: 300,
119
+ },
120
+ },
121
+ } as unknown as SessionStore;
122
+
123
+ it('should return an array of sessions sorted by pinned and updateAt fields', () => {
124
+ expect(sessionList(s)).toEqual([s.sessions['3'], s.sessions['1'], s.sessions['2']]);
125
+ });
126
+
127
+ it('should return an empty array when s.sessions is an empty object', () => {
128
+ const result = sessionList({
129
+ sessions: {},
130
+ } as any);
131
+ expect(result).toEqual([]);
132
+ });
133
+
134
+ it('should return a sorted session list when s.sessions is not empty object', () => {
135
+ const session1 = {
136
+ id: '1',
137
+ pinned: true,
138
+ updateAt: 1635705600000,
139
+ meta: {},
140
+ };
141
+ const session2 = {
142
+ id: '2',
143
+ pinned: false,
144
+ updateAt: 1635705600000,
145
+ meta: {},
146
+ };
147
+ const session3 = {
148
+ id: '3',
149
+ pinned: true,
150
+ updateAt: 1635705700000,
151
+ meta: {},
152
+ };
153
+ const s = {
154
+ sessions: {
155
+ '1': session1,
156
+ '2': session2,
157
+ '3': session3,
158
+ },
159
+ } as unknown as SessionStore;
160
+ const result = sessionList(s);
161
+ expect(result).toEqual([session3, session1, session2]);
162
+ });
163
+
164
+ it('should return a sorted session list when s.searchKeywords is not an empty string', () => {
165
+ const session1 = {
166
+ id: '1',
167
+ pinned: true,
168
+ meta: { title: 'yword' },
169
+
170
+ updateAt: 1635705600000,
171
+ };
172
+ const session2 = {
173
+ id: '2',
174
+ pinned: false,
175
+ meta: { title: 'keyword' },
176
+ updateAt: 1635705600000,
177
+ };
178
+ const session3 = {
179
+ id: '3',
180
+ pinned: true,
181
+ meta: { title: 'kyword' },
182
+ updateAt: 1635705800000,
183
+ };
184
+ const s = {
185
+ sessions: {
186
+ '1': session1,
187
+ '2': session2,
188
+ '3': session3,
189
+ },
190
+ searchKeywords: 'k',
191
+ } as unknown as SessionStore;
192
+
193
+ const result = sessionList(s);
194
+ expect(result).toEqual([session3, session2]);
195
+ });
196
+
197
+ it('should return an empty array when s.sessions is an empty object and s.searchKeywords is not an empty string', () => {
198
+ const result = sessionList({
199
+ sessions: {},
200
+ searchKeywords: 'keyword',
201
+ } as unknown as SessionStore);
202
+ expect(result).toEqual([]);
203
+ });
204
+ });
205
+
206
+ describe('hasSessionList', () => {
207
+ it('should return true when sessionList(s) returns a non-empty array', () => {
208
+ const s = {
209
+ sessions: {
210
+ '1': {},
211
+ },
212
+ } as unknown as SessionStore;
213
+ expect(hasSessionList(s)).toBe(true);
214
+ });
215
+
216
+ it('should return false when sessionList(s) returns an empty array', () => {
217
+ const s = {
218
+ sessions: {},
219
+ } as unknown as SessionStore;
220
+ expect(hasSessionList(s)).toBe(false);
221
+ });
222
+ });
223
+
224
+ describe('getSessionById', () => {
225
+ const s = {
226
+ activeId: '1',
227
+ sessions: {
228
+ '1': {
229
+ id: '1',
230
+ config: {
231
+ model: LanguageModel.GPT3_5,
232
+ params: {},
233
+ systemRole: 'system-role',
234
+ },
235
+ type: LobeSessionType.Agent,
236
+ } as LobeAgentSession,
237
+ '2': {
238
+ id: '2',
239
+ config: {
240
+ model: LanguageModel.GPT3_5,
241
+ params: {},
242
+ systemRole: 'system-role',
243
+ },
244
+ type: LobeSessionType.Agent,
245
+ } as LobeAgentSession,
246
+ },
247
+ inbox: {
248
+ id: 'inbox',
249
+ config: {
250
+ model: LanguageModel.GPT3_5,
251
+ params: {},
252
+ systemRole: 'system-role',
253
+ },
254
+ type: LobeSessionType.Agent,
255
+ } as LobeAgentSession,
256
+ } as unknown as SessionStore;
257
+
258
+ it('should return s.inbox when id is equal to INBOX_SESSION_ID', () => {
259
+ expect(getSessionById('inbox')(s)).toEqual(s.inbox);
260
+ });
261
+
262
+ it('should return the session with the specified id when id is not equal to INBOX_SESSION_ID', () => {
263
+ expect(getSessionById('1')(s)).toEqual(s.sessions['1']);
264
+ });
265
+
266
+ it('should return initLobeSession when the session with the specified id does not exist', () => {
267
+ expect(getSessionById('3')(s)).toEqual(initLobeSession);
268
+ });
269
+ });
270
+
271
+ describe('getSessionMetaById', () => {
272
+ const s: SessionStore = {
273
+ sessions: {
274
+ '1': {
275
+ meta: {
276
+ title: 'Session 1',
277
+ },
278
+ },
279
+ '2': {
280
+ meta: {
281
+ title: 'Session 2',
282
+ },
283
+ },
284
+ },
285
+ inbox: {
286
+ meta: {
287
+ title: 'Inbox',
288
+ },
289
+ },
290
+ } as unknown as SessionStore;
291
+
292
+ it('should return the meta data of the session with the specified id', () => {
293
+ expect(getSessionMetaById('1')(s)).toEqual({ title: 'Session 1' });
294
+ });
295
+
296
+ it('should return the meta data of the inbox session when id is equal to INBOX_SESSION_ID', () => {
297
+ expect(getSessionMetaById('inbox')(s)).toEqual({ title: 'Inbox' });
298
+ });
299
+
300
+ it('should return an empty object when the session with the specified id does not exist', () => {
301
+ expect(getSessionMetaById('3')(s)).toEqual({});
302
+ });
303
+ });
@@ -1,13 +1,16 @@
1
- import { SessionStore } from '@/store/session';
1
+ import { INBOX_SESSION_ID } from '@/const/session';
2
2
  import { MetaData } from '@/types/meta';
3
3
  import { LobeAgentSession } from '@/types/session';
4
4
  import { filterWithKeywords } from '@/utils/filter';
5
5
 
6
+ import { SessionStore } from '../../../store';
6
7
  import { initLobeSession } from '../initialState';
7
8
 
8
9
  export const currentSession = (s: SessionStore): LobeAgentSession | undefined => {
9
10
  if (!s.activeId) return;
10
11
 
12
+ if (s.activeId === INBOX_SESSION_ID) return s.inbox;
13
+
11
14
  return s.sessions[s.activeId];
12
15
  };
13
16
 
@@ -17,7 +20,7 @@ export const currentSessionSafe = (s: SessionStore): LobeAgentSession => {
17
20
 
18
21
  export const sessionList = (s: SessionStore) => {
19
22
  const filterChats = filterWithKeywords(s.sessions, s.searchKeywords, (item) => [
20
- Object.values(item.chats)
23
+ Object.values(item.chats || {})
21
24
  .map((c) => c.content)
22
25
  .join(''),
23
26
  ]);
@@ -44,6 +47,8 @@ export const hasSessionList = (s: SessionStore) => {
44
47
  export const getSessionById =
45
48
  (id: string) =>
46
49
  (s: SessionStore): LobeAgentSession => {
50
+ if (id === INBOX_SESSION_ID) return s.inbox;
51
+
47
52
  const session = s.sessions[id];
48
53
 
49
54
  if (!session) return initLobeSession;
@@ -53,7 +58,7 @@ export const getSessionById =
53
58
  export const getSessionMetaById =
54
59
  (id: string) =>
55
60
  (s: SessionStore): MetaData => {
56
- const session = s.sessions[id];
61
+ const session = getSessionById(id)(s);
57
62
 
58
63
  if (!session) return {};
59
64
  return session.meta;
@@ -6,9 +6,9 @@ import { StateCreator } from 'zustand/vanilla';
6
6
  import { isDev } from '@/utils/env';
7
7
 
8
8
  import { SessionStoreState, initialState } from './initialState';
9
- import { AgentAction, createAgentSlice } from './slices/agentConfig';
10
- import { ChatAction, createChatSlice } from './slices/chat';
11
- import { SessionAction, createSessionSlice } from './slices/session';
9
+ import { AgentAction, createAgentSlice } from './slices/agentConfig/action';
10
+ import { ChatAction, createChatSlice } from './slices/chat/actions';
11
+ import { SessionAction, createSessionSlice } from './slices/session/action';
12
12
 
13
13
  // =============== 聚合 createStoreFn ============ //
14
14
 
@@ -23,7 +23,7 @@ const createStore: StateCreator<SessionStore, [['zustand/devtools', never]]> = (
23
23
 
24
24
  // =============== persist 本地缓存中间件配置 ============ //
25
25
 
26
- type SessionPersist = Pick<SessionStore, 'sessions'>;
26
+ type SessionPersist = Pick<SessionStore, 'sessions' | 'inbox'>;
27
27
 
28
28
  const LOBE_CHAT = 'LOBE_CHAT';
29
29
 
@@ -31,13 +31,13 @@ const persistOptions: PersistOptions<SessionStore, SessionPersist> = {
31
31
  name: LOBE_CHAT,
32
32
 
33
33
  partialize: (s) => ({
34
+ inbox: s.inbox,
34
35
  sessions: s.sessions,
35
36
  }),
36
37
 
37
38
  // 手动控制 Hydration ,避免 ssr 报错
38
39
  skipHydration: true,
39
40
  version: 0,
40
- // version: Migration.targetVersion,
41
41
  };
42
42
 
43
43
  // =============== 实装 useStore ============ //