@lobehub/chat 0.105.2 → 0.107.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 (224) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +18 -0
  3. package/README.zh-CN.md +18 -0
  4. package/docs/Environment-Variable.md +4 -2
  5. package/docs/Environment-Variable.zh-CN.md +4 -2
  6. package/locales/en_US/common.json +19 -0
  7. package/locales/en_US/migration.json +45 -0
  8. package/locales/en_US/setting.json +7 -7
  9. package/locales/ja_JP/common.json +19 -0
  10. package/locales/ja_JP/migration.json +45 -0
  11. package/locales/ja_JP/setting.json +2 -2
  12. package/locales/ko_KR/common.json +19 -0
  13. package/locales/ko_KR/migration.json +45 -0
  14. package/locales/ko_KR/setting.json +2 -2
  15. package/locales/ru_RU/common.json +19 -0
  16. package/locales/ru_RU/migration.json +45 -0
  17. package/locales/ru_RU/setting.json +2 -2
  18. package/locales/zh_CN/common.json +19 -0
  19. package/locales/zh_CN/migration.json +45 -0
  20. package/locales/zh_CN/setting.json +1 -1
  21. package/locales/zh_TW/common.json +19 -0
  22. package/locales/zh_TW/migration.json +45 -0
  23. package/locales/zh_TW/setting.json +2 -2
  24. package/package.json +2 -1
  25. package/src/app/api/openai/chat/createChatCompletion.test.ts +4 -4
  26. package/src/app/api/openai/chat/createChatCompletion.ts +4 -10
  27. package/src/app/api/openai/createBizOpenAI/index.ts +1 -1
  28. package/src/app/api/openai/errorResponse.test.ts +1 -1
  29. package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +2 -1
  30. package/src/app/chat/(mobile)/features/ChatInput/SendButton.tsx +2 -2
  31. package/src/app/chat/features/ChatHeader/ShareButton/Inner.tsx +2 -5
  32. package/src/app/chat/features/ChatHeader/ShareButton/index.tsx +2 -2
  33. package/src/app/chat/features/ChatInput/ActionBar/Clear.tsx +2 -2
  34. package/src/app/chat/features/ChatInput/ActionBar/ModelSwitch.tsx +1 -1
  35. package/src/app/chat/features/ChatInput/ActionBar/Token/TokenTag.tsx +8 -2
  36. package/src/app/chat/features/ChatInput/InputAreaInner/index.tsx +2 -2
  37. package/src/app/chat/features/ChatInput/STT/index.tsx +2 -2
  38. package/src/app/chat/features/ChatInput/Topic/index.tsx +2 -2
  39. package/src/app/chat/features/ChatInput/useSend.ts +3 -3
  40. package/src/app/chat/features/Conversation/ChatList/Actions/index.ts +2 -2
  41. package/src/app/chat/features/Conversation/ChatList/Error/ApiKeyForm.tsx +2 -2
  42. package/src/app/chat/features/Conversation/ChatList/Error/InvalidAccess.tsx +2 -2
  43. package/src/app/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +2 -2
  44. package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.test.tsx +4 -4
  45. package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.tsx +30 -31
  46. package/src/app/chat/features/Conversation/ChatList/Extras/TTS.tsx +2 -2
  47. package/src/app/chat/features/Conversation/ChatList/Extras/Translate.tsx +2 -2
  48. package/src/app/chat/features/Conversation/ChatList/Extras/User.tsx +8 -5
  49. package/src/app/chat/features/Conversation/ChatList/Extras/index.ts +3 -3
  50. package/src/app/chat/features/Conversation/ChatList/Extras/type.ts +5 -0
  51. package/src/app/chat/features/Conversation/ChatList/Messages/Assistant.tsx +10 -6
  52. package/src/app/chat/features/Conversation/ChatList/Messages/Default.tsx +7 -3
  53. package/src/app/chat/features/Conversation/ChatList/Messages/Function.tsx +7 -6
  54. package/src/app/chat/features/Conversation/ChatList/Messages/index.ts +7 -7
  55. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/Iframe.tsx +6 -6
  56. package/src/app/chat/features/Conversation/ChatList/index.tsx +16 -24
  57. package/src/app/chat/features/Conversation/ScrollAnchor/index.tsx +2 -2
  58. package/src/app/chat/features/Conversation/index.tsx +3 -3
  59. package/src/app/chat/features/Conversation/useInitConversation.ts +43 -0
  60. package/src/app/chat/features/Migration/ExportConfigButton.tsx +33 -0
  61. package/src/app/chat/features/Migration/Failed.tsx +115 -0
  62. package/src/app/chat/features/Migration/Modal.tsx +80 -0
  63. package/src/app/chat/features/Migration/Start.tsx +106 -0
  64. package/src/app/chat/features/Migration/UpgradeButton.tsx +74 -0
  65. package/src/app/chat/features/Migration/const.ts +15 -0
  66. package/src/app/chat/features/Migration/index.tsx +44 -0
  67. package/src/app/chat/features/SessionListContent/DefaultMode.tsx +54 -0
  68. package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +5 -4
  69. package/src/app/chat/features/SessionListContent/List/Item/index.tsx +22 -28
  70. package/src/app/chat/features/SessionListContent/SearchMode.tsx +15 -0
  71. package/src/app/chat/features/SessionListContent/index.tsx +4 -44
  72. package/src/app/chat/features/SessionSearchBar/index.tsx +11 -3
  73. package/src/app/chat/features/TopicListContent/Header.tsx +4 -4
  74. package/src/app/chat/features/TopicListContent/Topic/TopicContent.tsx +11 -9
  75. package/src/app/chat/features/TopicListContent/Topic/TopicItem.tsx +15 -13
  76. package/src/app/chat/features/TopicListContent/Topic/index.tsx +9 -16
  77. package/src/app/chat/features/TopicListContent/TopicSearchBar/index.tsx +13 -4
  78. package/src/app/chat/features/TopicListContent/index.tsx +1 -2
  79. package/src/app/chat/page.tsx +6 -1
  80. package/src/app/chat/settings/features/HeaderContent.tsx +3 -3
  81. package/src/app/home/Redirect.tsx +13 -7
  82. package/src/app/settings/(mobile)/features/ExtraList.tsx +12 -4
  83. package/src/app/settings/common/Common.tsx +16 -4
  84. package/src/app/welcome/features/Banner/index.tsx +9 -10
  85. package/src/chains/summaryTitle.ts +5 -2
  86. package/src/components/DataStyleModal/index.tsx +64 -0
  87. package/src/config/client.ts +5 -0
  88. package/src/const/session.ts +21 -0
  89. package/src/const/settings.ts +1 -1
  90. package/src/database/core/__tests__/db.test.ts +8 -5
  91. package/src/database/core/__tests__/model.test.ts +3 -3
  92. package/src/database/core/db.ts +38 -10
  93. package/src/database/core/index.ts +1 -0
  94. package/src/database/core/model.ts +121 -10
  95. package/src/database/core/schemas.ts +22 -0
  96. package/src/database/core/types/db.ts +13 -0
  97. package/src/database/models/__DEBUG.ts +119 -0
  98. package/src/database/models/{file.test.ts → __tests__/file.test.ts} +19 -6
  99. package/src/database/models/__tests__/message.test.ts +314 -0
  100. package/src/database/models/__tests__/session.test.ts +117 -0
  101. package/src/database/models/__tests__/topic.test.ts +250 -0
  102. package/src/database/models/file.ts +9 -5
  103. package/src/database/models/message.ts +186 -0
  104. package/src/database/models/session.ts +160 -0
  105. package/src/database/models/topic.ts +196 -0
  106. package/src/{types/database → database/schemas}/files.ts +2 -2
  107. package/src/database/schemas/message.ts +37 -0
  108. package/src/database/schemas/session.ts +56 -0
  109. package/src/database/schemas/topic.ts +12 -0
  110. package/src/features/AgentSetting/AgentConfig/index.tsx +8 -1
  111. package/src/features/AgentSetting/AgentTTS/SelectWithTTSPreview.tsx +1 -1
  112. package/src/features/AgentSetting/store/action.ts +1 -1
  113. package/src/features/AgentSetting/store/initialState.ts +1 -1
  114. package/src/features/AgentSetting/store/reducers/config.ts +1 -1
  115. package/src/features/DataImporter/index.tsx +240 -0
  116. package/src/features/DebugUI/index.tsx +34 -0
  117. package/src/features/SideBar/BottomActions.tsx +8 -14
  118. package/src/hooks/useImportConfig.ts +13 -30
  119. package/src/hooks/useTTS.ts +1 -1
  120. package/src/layout/GlobalLayout/index.tsx +16 -1
  121. package/src/locales/default/common.ts +19 -0
  122. package/src/locales/default/migration.ts +47 -0
  123. package/src/locales/default/setting.ts +1 -1
  124. package/src/locales/resources/en_US.ts +2 -0
  125. package/src/locales/resources/ja_JP.ts +2 -0
  126. package/src/locales/resources/ko_KR.ts +2 -0
  127. package/src/locales/resources/ru_RU.ts +2 -0
  128. package/src/locales/resources/zh_CN.ts +2 -0
  129. package/src/locales/resources/zh_TW.ts +2 -0
  130. package/src/migrations/FromV0ToV1.ts +2 -4
  131. package/src/migrations/FromV1ToV2/fixtures/input-v1-session.json +191 -0
  132. package/src/migrations/FromV1ToV2/fixtures/output-v2.json +202 -0
  133. package/src/migrations/FromV1ToV2/index.ts +82 -0
  134. package/src/migrations/FromV1ToV2/migrations.test.ts +224 -0
  135. package/src/migrations/FromV1ToV2/types/v1.ts +78 -0
  136. package/src/migrations/FromV1ToV2/types/v2.ts +52 -0
  137. package/src/{utils → migrations}/VersionController.test.ts +1 -3
  138. package/src/{utils → migrations}/VersionController.ts +5 -2
  139. package/src/migrations/index.ts +17 -3
  140. package/src/services/__tests__/file.test.ts +3 -3
  141. package/src/services/chat.ts +66 -5
  142. package/src/services/config.ts +184 -0
  143. package/src/services/debug.ts +13 -0
  144. package/src/services/file.ts +6 -2
  145. package/src/services/github.ts +25 -0
  146. package/src/services/message.ts +79 -0
  147. package/src/services/session.ts +63 -0
  148. package/src/services/topic.ts +56 -0
  149. package/src/store/chat/actions/enhance.ts +95 -0
  150. package/src/store/{session/slices/chat → chat}/actions/message.ts +225 -247
  151. package/src/store/{session/slices/chat → chat}/actions/plugin.ts +29 -34
  152. package/src/store/{session/slices/chat → chat}/actions/share.ts +9 -13
  153. package/src/store/chat/actions/topic.ts +197 -0
  154. package/src/store/chat/helpers.ts +23 -0
  155. package/src/store/chat/index.ts +3 -0
  156. package/src/store/chat/initialState.ts +48 -0
  157. package/src/store/chat/reducers/message.test.ts +83 -0
  158. package/src/store/chat/reducers/message.ts +83 -0
  159. package/src/store/chat/selectors/__tests__/topic.test.ts +43 -0
  160. package/src/store/chat/selectors/chat.ts +119 -0
  161. package/src/store/chat/selectors/index.ts +2 -0
  162. package/src/store/chat/selectors/topic.ts +28 -0
  163. package/src/store/chat/store.ts +63 -0
  164. package/src/store/files/slices/images/action.ts +6 -4
  165. package/src/store/global/selectors/__snapshots__/settings.test.ts.snap +99 -0
  166. package/src/store/global/selectors/settings.test.ts +51 -0
  167. package/src/store/global/selectors/settings.ts +36 -5
  168. package/src/store/global/store.ts +1 -1
  169. package/src/store/middleware/createHyperStorage/index.ts +40 -24
  170. package/src/store/middleware/createHyperStorage/keyMapper.ts +8 -2
  171. package/src/store/middleware/createHyperStorage/type.ts +10 -8
  172. package/src/store/plugin/slices/plugin/action.ts +1 -1
  173. package/src/store/plugin/store.ts +2 -4
  174. package/src/store/session/helpers.ts +1 -1
  175. package/src/store/session/hooks/useSessionHydrated.ts +6 -10
  176. package/src/store/session/initialState.ts +26 -7
  177. package/src/store/session/selectors.ts +1 -2
  178. package/src/store/session/slices/agent/action.ts +26 -35
  179. package/src/store/session/slices/agent/selectors.ts +32 -37
  180. package/src/store/session/slices/session/action.ts +81 -88
  181. package/src/store/session/slices/session/helpers.ts +19 -0
  182. package/src/store/session/slices/session/initialState.ts +7 -10
  183. package/src/store/session/slices/session/selectors/export.ts +12 -9
  184. package/src/store/session/slices/session/selectors/index.ts +2 -35
  185. package/src/store/session/slices/session/selectors/list.test.ts +25 -203
  186. package/src/store/session/slices/session/selectors/list.ts +40 -78
  187. package/src/store/session/store.ts +11 -17
  188. package/src/types/agent/index.ts +61 -0
  189. package/src/types/chatMessage.ts +11 -20
  190. package/src/types/exportConfig.ts +7 -2
  191. package/src/types/files.ts +2 -2
  192. package/src/types/llm.ts +1 -1
  193. package/src/types/meta.ts +24 -12
  194. package/src/types/openai/chat.ts +9 -1
  195. package/src/types/openai/functionCall.ts +4 -0
  196. package/src/types/session.ts +10 -94
  197. package/src/types/settings.ts +2 -0
  198. package/src/types/translate.ts +2 -1
  199. package/src/utils/config.ts +5 -11
  200. package/src/utils/fetch.ts +8 -2
  201. package/src/app/chat/features/Conversation/usePluginsInit.ts +0 -14
  202. package/src/database/core/schema.ts +0 -9
  203. package/src/helpers/export.ts +0 -69
  204. package/src/hooks/useExportConfig.ts +0 -6
  205. package/src/store/session/slices/chat/actions/index.ts +0 -32
  206. package/src/store/session/slices/chat/actions/topic.ts +0 -171
  207. package/src/store/session/slices/chat/actions/translate.ts +0 -113
  208. package/src/store/session/slices/chat/helpers.ts +0 -9
  209. package/src/store/session/slices/chat/initialState.ts +0 -13
  210. package/src/store/session/slices/chat/reducers/message.test.ts +0 -319
  211. package/src/store/session/slices/chat/reducers/message.ts +0 -156
  212. package/src/store/session/slices/chat/reducers/topic.test.ts +0 -179
  213. package/src/store/session/slices/chat/reducers/topic.ts +0 -81
  214. package/src/store/session/slices/chat/selectors/chat.ts +0 -111
  215. package/src/store/session/slices/chat/selectors/index.ts +0 -26
  216. package/src/store/session/slices/chat/selectors/topic.ts +0 -30
  217. package/src/store/session/slices/chat/selectors/utils.test.ts +0 -385
  218. package/src/store/session/slices/chat/selectors/utils.ts +0 -91
  219. package/src/store/session/slices/chat/utils.ts +0 -13
  220. package/src/store/session/slices/session/reducers/session.test.ts +0 -577
  221. package/src/store/session/slices/session/reducers/session.ts +0 -182
  222. package/src/types/database/db.ts +0 -4
  223. /package/src/store/{session/slices/chat → chat}/reducers/files.test.ts +0 -0
  224. /package/src/store/{session/slices/chat → chat}/reducers/files.ts +0 -0
@@ -3,20 +3,13 @@ import { LanguageModel } from '@/types/llm';
3
3
  import { LobeAgentSession, LobeSessionType } from '@/types/session';
4
4
 
5
5
  import { initLobeSession } from '../initialState';
6
- import {
7
- currentSession,
8
- currentSessionSafe,
9
- getSessionById,
10
- getSessionMetaById,
11
- hasSessionList,
12
- sessionList,
13
- } from './list';
6
+ import { sessionSelectors } from './list';
14
7
 
15
8
  describe('currentSession', () => {
16
9
  const s = {
17
10
  activeId: '1',
18
- sessions: {
19
- '1': {
11
+ sessions: [
12
+ {
20
13
  id: '1',
21
14
  config: {
22
15
  model: LanguageModel.GPT3_5,
@@ -25,7 +18,7 @@ describe('currentSession', () => {
25
18
  },
26
19
  type: LobeSessionType.Agent,
27
20
  } as LobeAgentSession,
28
- '2': {
21
+ {
29
22
  id: '2',
30
23
  config: {
31
24
  model: LanguageModel.GPT3_5,
@@ -34,36 +27,23 @@ describe('currentSession', () => {
34
27
  },
35
28
  type: LobeSessionType.Agent,
36
29
  } 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,
30
+ ],
47
31
  } as unknown as SessionStore;
48
32
 
49
33
  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);
34
+ expect(sessionSelectors.currentSession({ sessions: {} } as any)).toBeUndefined();
55
35
  });
56
36
 
57
37
  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']);
38
+ expect(sessionSelectors.currentSession(s)).toEqual(s.sessions[0]);
59
39
  });
60
40
  });
61
41
 
62
42
  describe('currentSessionSafe', () => {
63
43
  const s = {
64
44
  activeId: '1',
65
- sessions: {
66
- '1': {
45
+ sessions: [
46
+ {
67
47
  id: '1',
68
48
  config: {
69
49
  model: LanguageModel.GPT3_5,
@@ -72,7 +52,7 @@ describe('currentSessionSafe', () => {
72
52
  },
73
53
  type: LobeSessionType.Agent,
74
54
  } as LobeAgentSession,
75
- '2': {
55
+ {
76
56
  id: '2',
77
57
  config: {
78
58
  model: LanguageModel.GPT3_5,
@@ -81,151 +61,23 @@ describe('currentSessionSafe', () => {
81
61
  },
82
62
  type: LobeSessionType.Agent,
83
63
  } 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,
64
+ ],
94
65
  } as unknown as SessionStore;
95
66
 
96
67
  it('should return initLobeSession when currentSession(s) returns undefined', () => {
97
- expect(currentSessionSafe({ sessions: {} } as any)).toEqual(initLobeSession);
68
+ expect(sessionSelectors.currentSessionSafe({ sessions: {} } as any)).toEqual(initLobeSession);
98
69
  });
99
70
 
100
71
  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);
72
+ expect(sessionSelectors.currentSessionSafe(s)).toEqual(s.sessions[0]);
221
73
  });
222
74
  });
223
75
 
224
76
  describe('getSessionById', () => {
225
77
  const s = {
226
78
  activeId: '1',
227
- sessions: {
228
- '1': {
79
+ sessions: [
80
+ {
229
81
  id: '1',
230
82
  config: {
231
83
  model: LanguageModel.GPT3_5,
@@ -234,7 +86,7 @@ describe('getSessionById', () => {
234
86
  },
235
87
  type: LobeSessionType.Agent,
236
88
  } as LobeAgentSession,
237
- '2': {
89
+ {
238
90
  id: '2',
239
91
  config: {
240
92
  model: LanguageModel.GPT3_5,
@@ -243,61 +95,31 @@ describe('getSessionById', () => {
243
95
  },
244
96
  type: LobeSessionType.Agent,
245
97
  } 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,
98
+ ],
256
99
  } as unknown as SessionStore;
257
100
 
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
101
  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']);
102
+ expect(sessionSelectors.getSessionById('1')(s)).toEqual(s.sessions[0]);
264
103
  });
265
104
 
266
105
  it('should return initLobeSession when the session with the specified id does not exist', () => {
267
- expect(getSessionById('3')(s)).toEqual(initLobeSession);
106
+ expect(sessionSelectors.getSessionById('3')(s)).toEqual(initLobeSession);
268
107
  });
269
108
  });
270
109
 
271
110
  describe('getSessionMetaById', () => {
272
111
  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
- },
112
+ sessions: [
113
+ { id: '1', meta: { title: 'Session 1' } },
114
+ { id: '2', meta: { title: 'Session 2' } },
115
+ ],
290
116
  } as unknown as SessionStore;
291
117
 
292
118
  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' });
119
+ expect(sessionSelectors.getSessionMetaById('1')(s)).toEqual({ title: 'Session 1' });
298
120
  });
299
121
 
300
122
  it('should return an empty object when the session with the specified id does not exist', () => {
301
- expect(getSessionMetaById('3')(s)).toEqual({});
123
+ expect(sessionSelectors.getSessionMetaById('3')(s)).toEqual({});
302
124
  });
303
125
  });
@@ -1,68 +1,27 @@
1
1
  import { INBOX_SESSION_ID } from '@/const/session';
2
+ import { sessionHelpers } from '@/store/session/slices/session/helpers';
2
3
  import { MetaData } from '@/types/meta';
3
- import { LobeAgentSession } from '@/types/session';
4
- import { filterWithKeywords } from '@/utils/filter';
4
+ import { LobeAgentSession, LobeSessions, SessionGroupDefaultKeys } from '@/types/session';
5
5
 
6
6
  import { SessionStore } from '../../../store';
7
7
  import { initLobeSession } from '../initialState';
8
8
 
9
- export const currentSession = (s: SessionStore): LobeAgentSession | undefined => {
10
- if (!s.activeId) return;
11
-
12
- if (s.activeId === INBOX_SESSION_ID) return s.inbox;
9
+ const defaultSessions = (s: SessionStore): LobeSessions => s.sessions;
13
10
 
14
- return s.sessions[s.activeId];
15
- };
11
+ const searchSessions = (s: SessionStore): LobeSessions => s.searchSessions;
16
12
 
17
- export const currentSessionSafe = (s: SessionStore): LobeAgentSession => {
18
- return currentSession(s) || initLobeSession;
19
- };
20
-
21
- export const sessionList = (s: SessionStore) => {
22
- const filterChats = filterWithKeywords(s.sessions, s.searchKeywords, (item) => [
23
- Object.values(item.chats || {})
24
- .map((c) => c.content)
25
- .join(''),
26
- ]);
27
- // 先按照 `pinned` 字段进行排序,置顶的会排在前面。然后,根据 `updateAt` 字段进行倒序排序。
28
- return Object.values(filterChats).sort((a, b) => {
29
- if (a.pinned && !b.pinned) {
30
- return -1;
31
- }
32
- if (!a.pinned && b.pinned) {
33
- return 1;
34
- }
35
- if (a.updateAt && b.updateAt) {
36
- return b.updateAt - a.updateAt;
37
- }
38
- return 0;
39
- });
40
- };
13
+ const pinnedSessionList = (s: SessionStore) =>
14
+ defaultSessions(s).filter((s) => s.group === SessionGroupDefaultKeys.Pinned);
41
15
 
42
- export const pinnedSessionList = (s: SessionStore) => sessionList(s).filter((s) => s.pinned);
43
- export const unpinnedSessionList = (s: SessionStore) => sessionList(s).filter((s) => !s.pinned);
44
-
45
- export const hasSessionList = (s: SessionStore) => {
46
- const list = sessionList(s);
47
- return list?.length > 0;
48
- };
49
- export const hasPinnedSessionList = (s: SessionStore) => {
50
- const list = pinnedSessionList(s);
51
- return list?.length > 0;
52
- };
16
+ const unpinnedSessionList = (s: SessionStore) =>
17
+ defaultSessions(s).filter((s) => s.group === SessionGroupDefaultKeys.Default);
53
18
 
54
- export const getSessionById =
19
+ const getSessionById =
55
20
  (id: string) =>
56
- (s: SessionStore): LobeAgentSession => {
57
- if (id === INBOX_SESSION_ID) return s.inbox;
21
+ (s: SessionStore): LobeAgentSession =>
22
+ sessionHelpers.getSessionById(id, s.sessions);
58
23
 
59
- const session = s.sessions[id];
60
-
61
- if (!session) return initLobeSession;
62
- return session;
63
- };
64
-
65
- export const getSessionMetaById =
24
+ const getSessionMetaById =
66
25
  (id: string) =>
67
26
  (s: SessionStore): MetaData => {
68
27
  const session = getSessionById(id)(s);
@@ -71,31 +30,34 @@ export const getSessionMetaById =
71
30
  return session.meta;
72
31
  };
73
32
 
74
- // export const sessionTreeSel = (s: SessionStore) => {
75
- // const sessionTree: SessionTree = [
76
- // {
77
- // agentId: 'default',
78
- // chats: chatListSel(s)
79
- // .filter((s) => !s.agentId)
80
- // .map((c) => c.id),
81
- // },
82
- // ];
83
- //
84
- // Object.values(s.agents).forEach((agent) => {
85
- // const chats = Object.values(s.chats).filter((s) => s.agentId === agent.id);
86
- //
87
- // sessionTree.push({
88
- // agentId: agent.id,
89
- // chats: chats.map((c) => c.id),
90
- // });
91
- // });
92
- //
93
- // return sessionTree;
94
- // };
33
+ const currentSession = (s: SessionStore): LobeAgentSession | undefined => {
34
+ if (!s.activeId) return;
35
+
36
+ return s.sessions.find((i) => i.id === s.activeId);
37
+ };
38
+
39
+ const currentSessionSafe = (s: SessionStore): LobeAgentSession => {
40
+ return currentSession(s) || initLobeSession;
41
+ };
95
42
 
96
- export const hasConversion = (s: SessionStore) => {
97
- const hasCustomAgents = !!Object.keys(s.sessions).length;
98
- const hasMessageInInbox = !!Object.keys(s.inbox.chats).length;
43
+ const hasPinnedSessionList = (s: SessionStore) => {
44
+ const list = pinnedSessionList(s);
45
+ return list?.length > 0;
46
+ };
99
47
 
100
- return hasCustomAgents || hasMessageInInbox;
48
+ const hasCustomAgents = (s: SessionStore) => defaultSessions(s).length > 0;
49
+
50
+ const isInboxSession = (s: SessionStore) => s.activeId === INBOX_SESSION_ID;
51
+
52
+ export const sessionSelectors = {
53
+ currentSession,
54
+ currentSessionSafe,
55
+ getSessionById,
56
+ getSessionMetaById,
57
+ hasCustomAgents,
58
+ hasPinnedSessionList,
59
+ isInboxSession,
60
+ pinnedSessionList,
61
+ searchSessions,
62
+ unpinnedSessionList,
101
63
  };
@@ -1,4 +1,4 @@
1
- import { PersistOptions, devtools, persist } from 'zustand/middleware';
1
+ import { PersistOptions, devtools, persist, subscribeWithSelector } from 'zustand/middleware';
2
2
  import { shallow } from 'zustand/shallow';
3
3
  import { createWithEqualityFn } from 'zustand/traditional';
4
4
  import { StateCreator } from 'zustand/vanilla';
@@ -8,54 +8,48 @@ import { isDev } from '@/utils/env';
8
8
  import { createHyperStorage } from '../middleware/createHyperStorage';
9
9
  import { SessionStoreState, initialState } from './initialState';
10
10
  import { AgentAction, createAgentSlice } from './slices/agent/action';
11
- import { ChatAction, createChatSlice } from './slices/chat/actions';
12
11
  import { SessionAction, createSessionSlice } from './slices/session/action';
13
12
 
14
13
  // =============== 聚合 createStoreFn ============ //
15
14
 
16
- export type SessionStore = SessionAction & AgentAction & ChatAction & SessionStoreState;
15
+ export type SessionStore = SessionAction & AgentAction & SessionStoreState;
17
16
  const createStore: StateCreator<SessionStore, [['zustand/devtools', never]]> = (...parameters) => ({
18
17
  ...initialState,
19
18
  ...createAgentSlice(...parameters),
20
19
  ...createSessionSlice(...parameters),
21
- ...createChatSlice(...parameters),
22
20
  });
23
21
 
24
22
  // =============== persist 本地缓存中间件配置 ============ //
25
23
 
26
- const LOBE_CHAT = 'LOBE_CHAT';
27
-
28
24
  const persistOptions: PersistOptions<SessionStore> = {
29
- name: LOBE_CHAT,
25
+ name: 'LOBE_CHAT',
30
26
 
31
27
  // 手动控制 Hydration ,避免 ssr 报错
32
28
  skipHydration: true,
33
29
 
34
30
  storage: createHyperStorage({
35
- localStorage: {
36
- dbName: 'LobeHub',
37
- mode: 'indexedDB',
38
- selectors: ['inbox', 'sessions'],
39
- },
31
+ localStorage: false,
40
32
  url: {
41
33
  mode: 'hash',
42
34
  selectors: [
43
35
  // map state key to storage key
44
36
  { activeId: 'session' },
45
- { activeTopicId: 'topic' },
46
37
  ],
47
38
  },
48
39
  }),
49
- version: 0,
40
+
41
+ version: 2,
50
42
  };
51
43
 
52
44
  // =============== 实装 useStore ============ //
53
45
 
54
46
  export const useSessionStore = createWithEqualityFn<SessionStore>()(
55
47
  persist(
56
- devtools(createStore, {
57
- name: LOBE_CHAT + (isDev ? '_DEV' : ''),
58
- }),
48
+ subscribeWithSelector(
49
+ devtools(createStore, {
50
+ name: 'LobeChat_Session' + (isDev ? '_DEV' : ''),
51
+ }),
52
+ ),
59
53
  persistOptions,
60
54
  ),
61
55
  shallow,
@@ -0,0 +1,61 @@
1
+ import { FewShots, LLMParams, LanguageModel } from '@/types/llm';
2
+
3
+ export type TTSServer = 'openai' | 'edge' | 'microsoft';
4
+
5
+ export interface LobeAgentTTSConfig {
6
+ showAllLocaleVoice?: boolean;
7
+ sttLocale: 'auto' | string;
8
+ ttsService: TTSServer;
9
+ voice: {
10
+ edge?: string;
11
+ microsoft?: string;
12
+ openai: string;
13
+ };
14
+ }
15
+ export interface LobeAgentConfig {
16
+ autoCreateTopicThreshold: number;
17
+ compressThreshold?: number;
18
+ displayMode?: 'chat' | 'docs';
19
+ enableAutoCreateTopic: boolean;
20
+ /**
21
+ * 历史消息长度压缩阈值
22
+ */
23
+ enableCompressThreshold?: boolean;
24
+ /**
25
+ * 开启历史记录条数
26
+ */
27
+ enableHistoryCount?: boolean;
28
+ enableMaxTokens?: boolean;
29
+
30
+ fewShots?: FewShots;
31
+ /**
32
+ * 历史消息条数
33
+ */
34
+ historyCount?: number;
35
+ inputTemplate?: string;
36
+ /**
37
+ * 角色所使用的语言模型
38
+ * @default gpt-3.5-turbo
39
+ */
40
+ model: LanguageModel | string;
41
+ /**
42
+ * 语言模型参数
43
+ */
44
+ params: LLMParams;
45
+ /**
46
+ * 启用的插件
47
+ */
48
+ plugins?: string[];
49
+ /**
50
+ * 系统角色
51
+ */
52
+ systemRole: string;
53
+ /**
54
+ * 语音服务
55
+ */
56
+ tts: LobeAgentTTSConfig;
57
+ }
58
+
59
+ export type LobeAgentConfigKeys =
60
+ | keyof LobeAgentConfig
61
+ | ['params', keyof LobeAgentConfig['params']];
@@ -1,5 +1,3 @@
1
- import { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
2
-
3
1
  import { ErrorType } from '@/types/fetch';
4
2
  import { Translate } from '@/types/translate';
5
3
 
@@ -14,10 +12,6 @@ export interface ChatMessageError {
14
12
  message: string;
15
13
  type: ErrorType;
16
14
  }
17
- export interface OpenAIFunctionCall {
18
- arguments?: string;
19
- name: string;
20
- }
21
15
 
22
16
  export interface ChatTranslate extends Translate {
23
17
  content?: string;
@@ -26,33 +20,29 @@ export interface ChatTranslate extends Translate {
26
20
  export interface ChatTTS {
27
21
  init?: boolean;
28
22
  }
23
+
24
+ export interface ChatPluginPayload {
25
+ apiName: string;
26
+ arguments: string;
27
+ identifier: string;
28
+ type: 'standalone' | 'default';
29
+ }
30
+
29
31
  export interface ChatMessage extends BaseDataModel {
30
- /**
31
- * @title 内容
32
- * @description 消息内容
33
- */
34
32
  content: string;
35
33
  error?: any;
36
34
  // 扩展字段
37
35
  extra?: {
38
36
  fromModel?: string;
39
37
  // 翻译
40
- translate?: ChatTranslate;
38
+ translate?: ChatTranslate | null;
41
39
  // TTS
42
40
  tts?: ChatTTS;
43
41
  } & Record<string, any>;
44
42
 
45
43
  files?: string[];
46
- /**
47
- * replace with plugin
48
- * @deprecated
49
- */
50
- function_call?: OpenAIFunctionCall;
51
-
52
- name?: string;
53
-
54
44
  parentId?: string;
55
- plugin?: PluginRequestPayload;
45
+ plugin?: ChatPluginPayload;
56
46
 
57
47
  pluginState?: any;
58
48
  // 引用
@@ -62,6 +52,7 @@ export interface ChatMessage extends BaseDataModel {
62
52
  * @description 消息发送者的角色
63
53
  */
64
54
  role: LLMRoleType;
55
+ sessionId?: string;
65
56
  /**
66
57
  * 保存到主题的消息
67
58
  */
@@ -1,5 +1,7 @@
1
- import { LobeAgentSession, LobeSessions } from '@/types/session';
1
+ import { ChatMessage } from '@/types/chatMessage';
2
+ import { LobeSessions } from '@/types/session';
2
3
  import { GlobalSettings } from '@/types/settings';
4
+ import { ChatTopic } from '@/types/topic';
3
5
 
4
6
  /**
5
7
  * 导出方式
@@ -40,15 +42,18 @@ export interface ConfigModelMap {
40
42
  * 配置状态:会话
41
43
  */
42
44
  export interface ConfigStateSessions {
43
- inbox: LobeAgentSession;
45
+ messages: ChatMessage[];
44
46
  sessions: LobeSessions;
47
+ topics: ChatTopic[];
45
48
  }
46
49
 
47
50
  /**
48
51
  * 配置状态:单个会话
49
52
  */
50
53
  export interface ConfigStateSingleSession {
54
+ messages: ChatMessage[];
51
55
  sessions: LobeSessions;
56
+ topics: ChatTopic[];
52
57
  }
53
58
 
54
59
  /**
@@ -1,6 +1,6 @@
1
- import { LocalFile } from './database/files';
1
+ import { DB_File } from '../database/schemas/files';
2
2
 
3
- export interface FilePreview extends Pick<LocalFile, 'saveMode' | 'fileType'> {
3
+ export interface FilePreview extends Pick<DB_File, 'saveMode' | 'fileType'> {
4
4
  base64Url?: string;
5
5
  data?: ArrayBuffer;
6
6
  name: string;