@lobehub/chat 1.0.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 (142) hide show
  1. package/.changelogrc.js +1 -0
  2. package/.commitlintrc.js +1 -0
  3. package/.editorconfig +16 -0
  4. package/.eslintignore +32 -0
  5. package/.eslintrc.js +6 -0
  6. package/.github/ISSUE_TEMPLATE/1_bug_report.yml +45 -0
  7. package/.github/ISSUE_TEMPLATE/2_feature_request.yml +21 -0
  8. package/.github/ISSUE_TEMPLATE/3_question.yml +15 -0
  9. package/.github/ISSUE_TEMPLATE/4_other.md +7 -0
  10. package/.github/PULL_REQUEST_TEMPLATE.md +17 -0
  11. package/.github/dependabot.yml +17 -0
  12. package/.github/workflows/auto-merge.yml +32 -0
  13. package/.github/workflows/contributor-help.yml +29 -0
  14. package/.github/workflows/issue-check-inactive.yml +22 -0
  15. package/.github/workflows/issue-close-require.yml +46 -0
  16. package/.github/workflows/issue-remove-inactive.yml +25 -0
  17. package/.github/workflows/release.yml +34 -0
  18. package/.github/workflows/test.yml +30 -0
  19. package/.gitpod.yml +3 -0
  20. package/.husky/commit-msg +4 -0
  21. package/.husky/pre-commit +5 -0
  22. package/.i18nrc.js +13 -0
  23. package/.prettierignore +63 -0
  24. package/.prettierrc.js +1 -0
  25. package/.releaserc.js +1 -0
  26. package/.remarkrc.js +1 -0
  27. package/.stylelintrc.js +8 -0
  28. package/CHANGELOG.md +80 -0
  29. package/README.md +147 -0
  30. package/locales/en_US/common.json +40 -0
  31. package/locales/en_US/setting.json +97 -0
  32. package/locales/zh_CN/common.json +40 -0
  33. package/locales/zh_CN/setting.json +98 -0
  34. package/next.config.mjs +32 -0
  35. package/package.json +138 -0
  36. package/public/next.svg +1 -0
  37. package/public/vercel.svg +1 -0
  38. package/scripts/genDefaultLocale.mjs +12 -0
  39. package/scripts/toc.mjs +40 -0
  40. package/src/const/fetch.ts +1 -0
  41. package/src/const/modelTokens.ts +8 -0
  42. package/src/features/FolderPanel/index.tsx +55 -0
  43. package/src/helpers/prompt.test.ts +36 -0
  44. package/src/helpers/prompt.ts +36 -0
  45. package/src/helpers/url.ts +17 -0
  46. package/src/layout/index.tsx +42 -0
  47. package/src/layout/style.ts +18 -0
  48. package/src/locales/create.ts +48 -0
  49. package/src/locales/default/common.ts +41 -0
  50. package/src/locales/default/setting.ts +97 -0
  51. package/src/locales/index.ts +5 -0
  52. package/src/locales/resources/en_US.ts +9 -0
  53. package/src/locales/resources/index.ts +7 -0
  54. package/src/locales/resources/zh_CN.ts +9 -0
  55. package/src/migrations/FromV0ToV1.ts +12 -0
  56. package/src/migrations/index.ts +13 -0
  57. package/src/pages/Sidebar.tsx +36 -0
  58. package/src/pages/_app.page.tsx +13 -0
  59. package/src/pages/_document.page.tsx +70 -0
  60. package/src/pages/api/LangChainStream.ts +95 -0
  61. package/src/pages/api/chain.api.ts +17 -0
  62. package/src/pages/api/openai.api.ts +31 -0
  63. package/src/pages/chat/SessionList/Header.tsx +56 -0
  64. package/src/pages/chat/SessionList/List/SessionItem.tsx +90 -0
  65. package/src/pages/chat/SessionList/List/index.tsx +31 -0
  66. package/src/pages/chat/SessionList/List/style.ts +77 -0
  67. package/src/pages/chat/SessionList/index.tsx +18 -0
  68. package/src/pages/chat/[id]/Config/ConfigCell.tsx +68 -0
  69. package/src/pages/chat/[id]/Config/ReadMode.tsx +63 -0
  70. package/src/pages/chat/[id]/Config/index.tsx +79 -0
  71. package/src/pages/chat/[id]/Conversation/ChatList.tsx +36 -0
  72. package/src/pages/chat/[id]/Conversation/Input.tsx +61 -0
  73. package/src/pages/chat/[id]/Conversation/index.tsx +32 -0
  74. package/src/pages/chat/[id]/Header.tsx +86 -0
  75. package/src/pages/chat/[id]/edit/AgentConfig.tsx +95 -0
  76. package/src/pages/chat/[id]/edit/AgentMeta.tsx +117 -0
  77. package/src/pages/chat/[id]/edit/FormItem.tsx +26 -0
  78. package/src/pages/chat/[id]/edit/Prompt.tsx +68 -0
  79. package/src/pages/chat/[id]/edit/index.page.tsx +62 -0
  80. package/src/pages/chat/[id]/edit/style.ts +42 -0
  81. package/src/pages/chat/[id]/index.page.tsx +40 -0
  82. package/src/pages/chat/index.page.tsx +1 -0
  83. package/src/pages/chat/layout.tsx +51 -0
  84. package/src/pages/index.page.tsx +1 -0
  85. package/src/pages/setting/Header.tsx +27 -0
  86. package/src/pages/setting/SettingForm.tsx +42 -0
  87. package/src/pages/setting/index.page.tsx +41 -0
  88. package/src/prompts/agent.ts +65 -0
  89. package/src/services/chatModel.ts +34 -0
  90. package/src/services/langChain.ts +18 -0
  91. package/src/services/url.ts +8 -0
  92. package/src/store/middleware/createHashStorage.ts +49 -0
  93. package/src/store/session/index.ts +33 -0
  94. package/src/store/session/initialState.ts +11 -0
  95. package/src/store/session/selectors.ts +3 -0
  96. package/src/store/session/slices/agentConfig/action.ts +226 -0
  97. package/src/store/session/slices/agentConfig/index.ts +3 -0
  98. package/src/store/session/slices/agentConfig/initialState.ts +34 -0
  99. package/src/store/session/slices/agentConfig/selectors.ts +54 -0
  100. package/src/store/session/slices/chat/action.ts +210 -0
  101. package/src/store/session/slices/chat/index.ts +3 -0
  102. package/src/store/session/slices/chat/initialState.ts +12 -0
  103. package/src/store/session/slices/chat/messageReducer.test.ts +70 -0
  104. package/src/store/session/slices/chat/messageReducer.ts +84 -0
  105. package/src/store/session/slices/chat/selectors.ts +83 -0
  106. package/src/store/session/slices/session/action.ts +118 -0
  107. package/src/store/session/slices/session/index.ts +3 -0
  108. package/src/store/session/slices/session/initialState.ts +31 -0
  109. package/src/store/session/slices/session/reducers/session.test.ts +456 -0
  110. package/src/store/session/slices/session/reducers/session.ts +113 -0
  111. package/src/store/session/slices/session/selectors/chat.ts +4 -0
  112. package/src/store/session/slices/session/selectors/index.ts +20 -0
  113. package/src/store/session/slices/session/selectors/list.ts +65 -0
  114. package/src/store/session/store.ts +17 -0
  115. package/src/store/settings/action.ts +31 -0
  116. package/src/store/settings/index.ts +23 -0
  117. package/src/store/settings/initialState.ts +25 -0
  118. package/src/store/settings/selectors.ts +9 -0
  119. package/src/store/settings/store.ts +13 -0
  120. package/src/styles/antdOverride.ts +29 -0
  121. package/src/styles/global.ts +23 -0
  122. package/src/styles/index.ts +6 -0
  123. package/src/types/chatMessage.ts +46 -0
  124. package/src/types/exportConfig.ts +23 -0
  125. package/src/types/global.d.ts +14 -0
  126. package/src/types/i18next.d.ts +8 -0
  127. package/src/types/langchain.ts +34 -0
  128. package/src/types/llm.ts +49 -0
  129. package/src/types/locale.ts +7 -0
  130. package/src/types/meta.ts +26 -0
  131. package/src/types/openai.ts +62 -0
  132. package/src/types/session.ts +59 -0
  133. package/src/utils/VersionController.test.ts +90 -0
  134. package/src/utils/VersionController.ts +64 -0
  135. package/src/utils/compass.ts +94 -0
  136. package/src/utils/fetch.ts +132 -0
  137. package/src/utils/filter.test.ts +120 -0
  138. package/src/utils/filter.ts +29 -0
  139. package/src/utils/uploadFIle.ts +8 -0
  140. package/src/utils/uuid.ts +9 -0
  141. package/tsconfig.json +26 -0
  142. package/vitest.config.ts +11 -0
@@ -0,0 +1,456 @@
1
+ import { produce } from 'immer';
2
+
3
+ import { ChatMessage } from '@/types/chatMessage';
4
+ import { LanguageModel } from '@/types/llm';
5
+ import { MetaData } from '@/types/meta';
6
+ import { LobeAgentConfig, LobeAgentSession, LobeSessionType, LobeSessions } from '@/types/session';
7
+
8
+ import { SessionDispatch, sessionsReducer } from './session';
9
+
10
+ describe('sessionsReducer', () => {
11
+ describe('addSession', () => {
12
+ it('should add session to state when valid session and type are provided', () => {
13
+ const state: LobeSessions = {};
14
+ const session = {
15
+ id: 'session-id',
16
+ config: {
17
+ model: LanguageModel.GPT3_5,
18
+ params: {},
19
+ systemRole: 'system-role',
20
+ },
21
+ type: LobeSessionType.Agent,
22
+ } as LobeAgentSession;
23
+ const payload: SessionDispatch = {
24
+ type: 'addSession',
25
+ session,
26
+ };
27
+
28
+ const newState = sessionsReducer(state, payload);
29
+
30
+ expect(newState).toEqual({
31
+ 'session-id': session,
32
+ });
33
+ });
34
+
35
+ it('should not change state when invalid session and type are provided', () => {
36
+ const state: LobeSessions = {};
37
+ const session = undefined as unknown as LobeAgentSession;
38
+ const payload: SessionDispatch = {
39
+ type: 'addSession',
40
+ session,
41
+ };
42
+
43
+ const newState = sessionsReducer(state, payload);
44
+
45
+ expect(newState).toEqual(state);
46
+ });
47
+ });
48
+
49
+ describe('removeSession', () => {
50
+ it('should remove session from state when valid id and type are provided', () => {
51
+ const state = {
52
+ 'session-id': {
53
+ id: 'session-id',
54
+ config: {
55
+ model: 'gpt-3.5-turbo',
56
+ params: {},
57
+ systemRole: 'system-role',
58
+ },
59
+ type: 'agent',
60
+ } as LobeAgentSession,
61
+ } as LobeSessions;
62
+ const id = 'session-id';
63
+ const payload: SessionDispatch = {
64
+ type: 'removeSession',
65
+ id,
66
+ };
67
+
68
+ const newState = sessionsReducer(state, payload);
69
+
70
+ expect(newState).toEqual({});
71
+ });
72
+
73
+ it('should not change state when invalid id and type are provided', () => {
74
+ const state: LobeSessions = {
75
+ 'session-id': {
76
+ id: 'session-id',
77
+ config: {
78
+ model: 'gpt-3.5-turbo',
79
+ params: {},
80
+ systemRole: 'system-role',
81
+ },
82
+ type: 'agent',
83
+ } as LobeAgentSession,
84
+ };
85
+ const id = 'non-existent-id';
86
+ const payload: SessionDispatch = {
87
+ type: 'removeSession',
88
+ id,
89
+ };
90
+
91
+ const newState = sessionsReducer(state, payload);
92
+
93
+ expect(newState).toEqual(state);
94
+ });
95
+ });
96
+
97
+ describe('updateSessionMeta', () => {
98
+ it('should update session meta when valid id, key, and value are provided', () => {
99
+ const state: LobeSessions = {
100
+ 'session-id': {
101
+ id: 'session-id',
102
+ config: {
103
+ model: 'gpt-3.5-turbo',
104
+ params: {},
105
+ systemRole: 'system-role',
106
+ },
107
+ type: 'agent',
108
+ meta: {
109
+ avatar: 'avatar-url',
110
+ backgroundColor: 'background-color',
111
+ description: 'description',
112
+ tag: ['tag1', 'tag2'],
113
+ title: 'title',
114
+ },
115
+ } as LobeAgentSession,
116
+ };
117
+ const id = 'session-id';
118
+ const key: keyof MetaData = 'title';
119
+ const value = 'new-title';
120
+ const payload: SessionDispatch = {
121
+ type: 'updateSessionMeta',
122
+ id,
123
+ key,
124
+ value,
125
+ };
126
+
127
+ const newState = sessionsReducer(state, payload);
128
+
129
+ expect(newState).toEqual({
130
+ 'session-id': {
131
+ ...state['session-id'],
132
+ meta: {
133
+ ...state['session-id'].meta,
134
+ title: 'new-title',
135
+ },
136
+ },
137
+ });
138
+ });
139
+
140
+ it('should not change state when invalid id, key, and value are provided', () => {
141
+ const state: LobeSessions = {
142
+ 'session-id': {
143
+ id: 'session-id',
144
+ config: {
145
+ model: 'gpt-3.5-turbo',
146
+ params: {},
147
+ systemRole: 'system-role',
148
+ },
149
+ type: 'agent',
150
+ meta: {
151
+ avatar: 'avatar-url',
152
+ backgroundColor: 'background-color',
153
+ description: 'description',
154
+ tag: ['tag1', 'tag2'],
155
+ title: 'title',
156
+ },
157
+ } as LobeAgentSession,
158
+ };
159
+ const id = 'non-existent-id';
160
+ const key = 'invalid-key' as keyof MetaData;
161
+ const value = 'new-value';
162
+ const payload: SessionDispatch = {
163
+ type: 'updateSessionMeta',
164
+ id,
165
+ key,
166
+ value,
167
+ };
168
+
169
+ const newState = sessionsReducer(state, payload);
170
+
171
+ expect(newState).toEqual(state);
172
+ });
173
+
174
+ it('should not change state when valid id, invalid key, and value are provided', () => {
175
+ const state: LobeSessions = {
176
+ 'session-id': {
177
+ id: 'session-id',
178
+ config: {
179
+ model: 'gpt-3.5-turbo',
180
+ params: {},
181
+ systemRole: 'system-role',
182
+ },
183
+ type: 'agent',
184
+ meta: {
185
+ avatar: 'avatar-url',
186
+ backgroundColor: 'background-color',
187
+ description: 'description',
188
+ tag: ['tag1', 'tag2'],
189
+ title: 'title',
190
+ },
191
+ } as LobeAgentSession,
192
+ };
193
+ const id = 'session-id';
194
+ const key = 'invalid-key' as keyof MetaData;
195
+ const value = 'new-value';
196
+ const payload: SessionDispatch = {
197
+ type: 'updateSessionMeta',
198
+ id,
199
+ key,
200
+ value,
201
+ };
202
+
203
+ const newState = sessionsReducer(state, payload);
204
+
205
+ expect(newState).toEqual(state);
206
+ });
207
+ });
208
+
209
+ describe('updateSessionConfig', () => {
210
+ it('should update session config when valid id and partial config are provided', () => {
211
+ const state: LobeSessions = {
212
+ 'session-id': {
213
+ id: 'session-id',
214
+ config: {
215
+ model: 'gpt-3.5-turbo',
216
+ params: {},
217
+ systemRole: 'system-role',
218
+ },
219
+ type: 'agent',
220
+ meta: {
221
+ avatar: 'avatar-url',
222
+ backgroundColor: 'background-color',
223
+ description: 'description',
224
+ tag: ['tag1', 'tag2'],
225
+ title: 'title',
226
+ },
227
+ } as LobeAgentSession,
228
+ };
229
+ const id = 'session-id';
230
+ const config: Partial<LobeAgentConfig> = {
231
+ model: LanguageModel.GPT4,
232
+ };
233
+ const payload: SessionDispatch = {
234
+ type: 'updateSessionConfig',
235
+ id,
236
+ config,
237
+ };
238
+
239
+ const newState = sessionsReducer(state, payload);
240
+
241
+ expect(newState).toEqual({
242
+ 'session-id': {
243
+ ...state['session-id'],
244
+ config: {
245
+ ...state['session-id'].config,
246
+ model: 'gpt-4',
247
+ },
248
+ },
249
+ });
250
+ });
251
+
252
+ it('should update session agent config correctly', () => {
253
+ const state: LobeSessions = {
254
+ session1: {
255
+ id: 'session1',
256
+ config: {
257
+ model: 'gpt-3.5-turbo',
258
+ params: {},
259
+ systemRole: 'system',
260
+ },
261
+ } as LobeAgentSession,
262
+ session2: {
263
+ id: 'session2',
264
+ config: {
265
+ model: 'gpt-3.5-turbo',
266
+ params: {},
267
+ systemRole: 'system',
268
+ },
269
+ } as LobeAgentSession,
270
+ };
271
+
272
+ const payload: SessionDispatch = {
273
+ type: 'updateSessionConfig',
274
+ id: 'session1',
275
+ config: {
276
+ model: LanguageModel.GPT4,
277
+ params: {},
278
+ },
279
+ };
280
+
281
+ const expectedState = produce(state, (draft) => {
282
+ draft.session1.config = {
283
+ model: LanguageModel.GPT4,
284
+ params: {},
285
+ systemRole: 'system',
286
+ };
287
+ });
288
+
289
+ const newState = sessionsReducer(state, payload);
290
+
291
+ expect(newState).toEqual(expectedState);
292
+ });
293
+
294
+ it('should not change state when invalid session ID is provided for updating session agent config', () => {
295
+ const state: LobeSessions = {
296
+ session1: {
297
+ id: 'session1',
298
+ config: {
299
+ model: 'gpt-3.5-turbo',
300
+ params: {},
301
+ systemRole: 'system',
302
+ },
303
+ } as LobeAgentSession,
304
+ };
305
+
306
+ const payload: SessionDispatch = {
307
+ type: 'updateSessionConfig',
308
+ id: 'session2',
309
+ config: {
310
+ model: LanguageModel.GPT4,
311
+ params: {},
312
+ },
313
+ };
314
+
315
+ const newState = sessionsReducer(state, payload);
316
+
317
+ expect(newState).toEqual(state);
318
+ });
319
+
320
+ it.skip('should not change state when invalid session agent config is provided for updating session agent config', () => {
321
+ const state: LobeSessions = {
322
+ session1: {
323
+ id: 'session1',
324
+ config: {
325
+ model: 'gpt-3.5-turbo',
326
+ params: {},
327
+ systemRole: 'system',
328
+ },
329
+ } as LobeAgentSession,
330
+ };
331
+
332
+ const payload: SessionDispatch = {
333
+ type: 'updateSessionConfig',
334
+ id: 'session1',
335
+ config: {
336
+ model: LanguageModel.GPT4,
337
+ params: {},
338
+ invalidKey: 'invalidValue',
339
+ } as unknown as LobeAgentConfig,
340
+ };
341
+
342
+ const newState = sessionsReducer(state, payload);
343
+
344
+ expect(newState).toEqual(state);
345
+ });
346
+ });
347
+
348
+ test('should not change state when invalid operation type is provided', () => {
349
+ const state: LobeSessions = {
350
+ session1: {
351
+ id: 'session1',
352
+ config: {
353
+ model: 'gpt-3.5-turbo',
354
+ params: {},
355
+ systemRole: 'system',
356
+ },
357
+ } as LobeAgentSession,
358
+ };
359
+
360
+ const payload = {
361
+ type: 'invalidOperation',
362
+ } as unknown as SessionDispatch;
363
+
364
+ const newState = sessionsReducer(state, payload);
365
+
366
+ expect(newState).toEqual(state);
367
+ });
368
+
369
+ describe('updateSessionChat', () => {
370
+ it('should update session chat correctly', () => {
371
+ const state: LobeSessions = {
372
+ session1: {
373
+ id: 'session1',
374
+ chats: {},
375
+ } as LobeAgentSession,
376
+ session2: {
377
+ id: 'session2',
378
+ chats: {},
379
+ } as LobeAgentSession,
380
+ };
381
+
382
+ const payload: SessionDispatch = {
383
+ type: 'updateSessionChat',
384
+ id: 'session1',
385
+ chats: {
386
+ message1: {
387
+ id: 'message1',
388
+ content: 'Hello',
389
+ } as ChatMessage,
390
+ },
391
+ };
392
+
393
+ const expectedState = produce(state, (draft) => {
394
+ draft.session1.chats = {
395
+ message1: {
396
+ id: 'message1',
397
+ content: 'Hello',
398
+ } as ChatMessage,
399
+ };
400
+ });
401
+
402
+ const newState = sessionsReducer(state, payload);
403
+
404
+ expect(newState).toEqual(expectedState);
405
+ });
406
+
407
+ it('should not change state when invalid session ID is provided for updating session chat', () => {
408
+ const state: LobeSessions = {
409
+ session1: {
410
+ id: 'session1',
411
+ chats: {},
412
+ } as LobeAgentSession,
413
+ };
414
+
415
+ const payload: SessionDispatch = {
416
+ type: 'updateSessionChat',
417
+ id: 'session2',
418
+ chats: {
419
+ message1: {
420
+ id: 'message1',
421
+ content: 'Hello',
422
+ } as ChatMessage,
423
+ },
424
+ };
425
+
426
+ const newState = sessionsReducer(state, payload);
427
+
428
+ expect(newState).toEqual(state);
429
+ });
430
+
431
+ it.skip('should not change state when invalid chat data is provided for updating session chat', () => {
432
+ const state: LobeSessions = {
433
+ session1: {
434
+ id: 'session1',
435
+ chats: {},
436
+ } as LobeAgentSession,
437
+ };
438
+
439
+ const payload: SessionDispatch = {
440
+ type: 'updateSessionChat',
441
+ id: 'session1',
442
+ chats: {
443
+ message1: {
444
+ id: 'message1',
445
+ content: 'Hello',
446
+ invalidKey: 'invalidValue',
447
+ } as unknown as ChatMessage,
448
+ },
449
+ };
450
+
451
+ const newState = sessionsReducer(state, payload);
452
+
453
+ expect(newState).toEqual(state);
454
+ });
455
+ });
456
+ });
@@ -0,0 +1,113 @@
1
+ import { produce } from 'immer';
2
+
3
+ import { ChatMessageMap } from '@/types/chatMessage';
4
+ import { MetaData } from '@/types/meta';
5
+ import { LobeAgentConfig, LobeAgentSession, LobeSessions } from '@/types/session';
6
+
7
+ /**
8
+ * @title 添加会话
9
+ */
10
+ interface AddSession {
11
+ /**
12
+ * @param session - 会话信息
13
+ */
14
+ session: LobeAgentSession;
15
+ /**
16
+ * @param type - 操作类型
17
+ * @default 'addChat'
18
+ */
19
+ type: 'addSession';
20
+ }
21
+
22
+ interface RemoveSession {
23
+ id: string;
24
+ type: 'removeSession';
25
+ }
26
+
27
+ /**
28
+ * @title 更新会话聊天上下文
29
+ */
30
+ interface UpdateSessionChat {
31
+ chats: ChatMessageMap;
32
+ /**
33
+ * 会话 ID
34
+ */
35
+ id: string;
36
+
37
+ type: 'updateSessionChat';
38
+ }
39
+
40
+ interface UpdateSessionMeta {
41
+ id: string;
42
+ key: keyof MetaData;
43
+ type: 'updateSessionMeta';
44
+ value: any;
45
+ }
46
+
47
+ interface UpdateSessionAgentConfig {
48
+ config: Partial<LobeAgentConfig>;
49
+ id: string;
50
+ type: 'updateSessionConfig';
51
+ }
52
+
53
+ export type SessionDispatch =
54
+ | AddSession
55
+ | UpdateSessionChat
56
+ | RemoveSession
57
+ | UpdateSessionMeta
58
+ | UpdateSessionAgentConfig;
59
+
60
+ export const sessionsReducer = (state: LobeSessions, payload: SessionDispatch): LobeSessions => {
61
+ switch (payload.type) {
62
+ case 'addSession': {
63
+ return produce(state, (draft) => {
64
+ const { session } = payload;
65
+ if (!session) return;
66
+
67
+ draft[session.id] = session;
68
+ });
69
+ }
70
+
71
+ case 'removeSession': {
72
+ return produce(state, (draft) => {
73
+ delete draft[payload.id];
74
+ });
75
+ }
76
+
77
+ case 'updateSessionMeta': {
78
+ return produce(state, (draft) => {
79
+ const chat = draft[payload.id];
80
+ if (!chat) return;
81
+
82
+ const { key, value } = payload;
83
+
84
+ const validKeys = ['avatar', 'backgroundColor', 'description', 'tag', 'title'];
85
+
86
+ if (validKeys.includes(key)) chat.meta[key] = value;
87
+ });
88
+ }
89
+
90
+ case 'updateSessionChat': {
91
+ return produce(state, (draft) => {
92
+ const chat = draft[payload.id];
93
+ if (!chat) return;
94
+
95
+ chat.chats = payload.chats;
96
+ });
97
+ }
98
+
99
+ case 'updateSessionConfig': {
100
+ return produce(state, (draft) => {
101
+ const { id, config } = payload;
102
+ const chat = draft[id];
103
+ if (!chat) return;
104
+
105
+ chat.config = { ...chat.config, ...config };
106
+ });
107
+ }
108
+
109
+ default: {
110
+ return produce(state, () => {});
111
+ }
112
+ }
113
+ };
@@ -0,0 +1,4 @@
1
+ import { DEFAULT_AVATAR } from '@/store/session/slices/agentConfig';
2
+ import { MetaData } from '@/types/meta';
3
+
4
+ export const getAgentAvatar = (s: MetaData) => s.avatar || DEFAULT_AVATAR;
@@ -0,0 +1,20 @@
1
+ import { getAgentAvatar } from './chat';
2
+ import {
3
+ chatListSel,
4
+ currentSessionSafe,
5
+ currentSessionSel,
6
+ getSessionById,
7
+ getSessionMetaById,
8
+ } from './list';
9
+
10
+ export const sessionSelectors = {
11
+ chatList: chatListSel,
12
+ currentSession: currentSessionSel,
13
+ currentSessionSafe,
14
+
15
+ getAgentAvatar,
16
+
17
+ getSessionById,
18
+ // sessionTree: sessionTreeSel,
19
+ getSessionMetaById,
20
+ };
@@ -0,0 +1,65 @@
1
+ import { SessionStore } from '@/store/session';
2
+ import { MetaData } from '@/types/meta';
3
+ import { LobeAgentSession } from '@/types/session';
4
+ import { filterWithKeywords } from '@/utils/filter';
5
+
6
+ import { initLobeSession } from '../initialState';
7
+
8
+ export const currentSessionSel = (s: SessionStore): LobeAgentSession | undefined => {
9
+ if (!s.activeId) return;
10
+
11
+ return s.sessions[s.activeId];
12
+ };
13
+ export const currentSessionSafe = (s: SessionStore): LobeAgentSession => {
14
+ return currentSessionSel(s) || initLobeSession;
15
+ };
16
+
17
+ export const chatListSel = (s: SessionStore) => {
18
+ const filterChats = filterWithKeywords(s.sessions, s.searchKeywords, (item) => [
19
+ Object.values(item.chats)
20
+ .map((c) => c.content)
21
+ .join(''),
22
+ ]);
23
+
24
+ return Object.values(filterChats).sort((a, b) => (b.updateAt || 0) - (a.updateAt || 0));
25
+ };
26
+
27
+ export const getSessionById =
28
+ (id: string) =>
29
+ (s: SessionStore): LobeAgentSession => {
30
+ const session = s.sessions[id];
31
+
32
+ if (!session) return initLobeSession;
33
+ return session;
34
+ };
35
+
36
+ export const getSessionMetaById =
37
+ (id: string) =>
38
+ (s: SessionStore): MetaData => {
39
+ const session = s.sessions[id];
40
+
41
+ if (!session) return {};
42
+ return session.meta;
43
+ };
44
+
45
+ // export const sessionTreeSel = (s: SessionStore) => {
46
+ // const sessionTree: SessionTree = [
47
+ // {
48
+ // agentId: 'default',
49
+ // chats: chatListSel(s)
50
+ // .filter((s) => !s.agentId)
51
+ // .map((c) => c.id),
52
+ // },
53
+ // ];
54
+ //
55
+ // Object.values(s.agents).forEach((agent) => {
56
+ // const chats = Object.values(s.chats).filter((s) => s.agentId === agent.id);
57
+ //
58
+ // sessionTree.push({
59
+ // agentId: agent.id,
60
+ // chats: chats.map((c) => c.id),
61
+ // });
62
+ // });
63
+ //
64
+ // return sessionTree;
65
+ // };
@@ -0,0 +1,17 @@
1
+ import { StateCreator } from 'zustand/vanilla';
2
+
3
+ import { SessionStoreState, initialState } from './initialState';
4
+ import { AgentAction, createAgentSlice } from './slices/agentConfig';
5
+ import { ChatAction, createChatSlice } from './slices/chat';
6
+ import { SessionAction, createSessionSlice } from './slices/session';
7
+
8
+ export type SessionStore = SessionAction & AgentAction & ChatAction & SessionStoreState;
9
+
10
+ export const createStore: StateCreator<SessionStore, [['zustand/devtools', never]]> = (
11
+ ...parameters
12
+ ) => ({
13
+ ...initialState,
14
+ ...createAgentSlice(...parameters),
15
+ ...createSessionSlice(...parameters),
16
+ ...createChatSlice(...parameters),
17
+ });
@@ -0,0 +1,31 @@
1
+ import { merge } from 'lodash-es';
2
+ import type { StateCreator } from 'zustand/vanilla';
3
+
4
+ import type { ConfigSettings } from '@/types/exportConfig';
5
+
6
+ import type { SidebarTabKey } from './initialState';
7
+ import { SettingsState } from './initialState';
8
+ import type { SettingsStore } from './store';
9
+
10
+ export interface SettingsAction {
11
+ importSettings: (settings: SettingsState) => void;
12
+ saveSettings: (settings: ConfigSettings) => void;
13
+ switchSideBar: (key: SidebarTabKey) => void;
14
+ }
15
+
16
+ export const createSettings: StateCreator<
17
+ SettingsStore,
18
+ [['zustand/devtools', never]],
19
+ [],
20
+ SettingsAction
21
+ > = (set, get) => ({
22
+ importSettings: (settings) => {
23
+ set({ ...settings });
24
+ },
25
+ saveSettings: (settings) => {
26
+ set({ settings: merge(get().settings, settings) });
27
+ },
28
+ switchSideBar: (key) => {
29
+ set({ sidebarKey: key });
30
+ },
31
+ });