@lobehub/chat 0.106.0 → 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 (214) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/locales/en_US/common.json +19 -0
  3. package/locales/en_US/migration.json +45 -0
  4. package/locales/en_US/setting.json +7 -7
  5. package/locales/ja_JP/common.json +19 -0
  6. package/locales/ja_JP/migration.json +45 -0
  7. package/locales/ja_JP/setting.json +2 -2
  8. package/locales/ko_KR/common.json +19 -0
  9. package/locales/ko_KR/migration.json +45 -0
  10. package/locales/ko_KR/setting.json +2 -2
  11. package/locales/ru_RU/common.json +19 -0
  12. package/locales/ru_RU/migration.json +45 -0
  13. package/locales/ru_RU/setting.json +2 -2
  14. package/locales/zh_CN/common.json +19 -0
  15. package/locales/zh_CN/migration.json +45 -0
  16. package/locales/zh_CN/setting.json +1 -1
  17. package/locales/zh_TW/common.json +19 -0
  18. package/locales/zh_TW/migration.json +45 -0
  19. package/locales/zh_TW/setting.json +2 -2
  20. package/package.json +2 -1
  21. package/src/app/api/openai/chat/createChatCompletion.test.ts +4 -4
  22. package/src/app/api/openai/chat/createChatCompletion.ts +4 -10
  23. package/src/app/api/openai/createBizOpenAI/index.ts +1 -1
  24. package/src/app/api/openai/errorResponse.test.ts +1 -1
  25. package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +2 -1
  26. package/src/app/chat/(mobile)/features/ChatInput/SendButton.tsx +2 -2
  27. package/src/app/chat/features/ChatHeader/ShareButton/Inner.tsx +2 -5
  28. package/src/app/chat/features/ChatHeader/ShareButton/index.tsx +2 -2
  29. package/src/app/chat/features/ChatInput/ActionBar/Clear.tsx +2 -2
  30. package/src/app/chat/features/ChatInput/ActionBar/Token/TokenTag.tsx +8 -2
  31. package/src/app/chat/features/ChatInput/InputAreaInner/index.tsx +2 -2
  32. package/src/app/chat/features/ChatInput/STT/index.tsx +2 -2
  33. package/src/app/chat/features/ChatInput/Topic/index.tsx +2 -2
  34. package/src/app/chat/features/ChatInput/useSend.ts +3 -3
  35. package/src/app/chat/features/Conversation/ChatList/Actions/index.ts +2 -2
  36. package/src/app/chat/features/Conversation/ChatList/Error/ApiKeyForm.tsx +2 -2
  37. package/src/app/chat/features/Conversation/ChatList/Error/InvalidAccess.tsx +2 -2
  38. package/src/app/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +2 -2
  39. package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.test.tsx +4 -4
  40. package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.tsx +30 -31
  41. package/src/app/chat/features/Conversation/ChatList/Extras/TTS.tsx +2 -2
  42. package/src/app/chat/features/Conversation/ChatList/Extras/Translate.tsx +2 -2
  43. package/src/app/chat/features/Conversation/ChatList/Extras/User.tsx +8 -5
  44. package/src/app/chat/features/Conversation/ChatList/Extras/index.ts +3 -3
  45. package/src/app/chat/features/Conversation/ChatList/Extras/type.ts +5 -0
  46. package/src/app/chat/features/Conversation/ChatList/Messages/Assistant.tsx +10 -6
  47. package/src/app/chat/features/Conversation/ChatList/Messages/Default.tsx +7 -3
  48. package/src/app/chat/features/Conversation/ChatList/Messages/Function.tsx +7 -6
  49. package/src/app/chat/features/Conversation/ChatList/Messages/index.ts +7 -7
  50. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/Iframe.tsx +6 -6
  51. package/src/app/chat/features/Conversation/ChatList/index.tsx +16 -24
  52. package/src/app/chat/features/Conversation/ScrollAnchor/index.tsx +2 -2
  53. package/src/app/chat/features/Conversation/index.tsx +3 -3
  54. package/src/app/chat/features/Conversation/useInitConversation.ts +43 -0
  55. package/src/app/chat/features/Migration/ExportConfigButton.tsx +33 -0
  56. package/src/app/chat/features/Migration/Failed.tsx +115 -0
  57. package/src/app/chat/features/Migration/Modal.tsx +80 -0
  58. package/src/app/chat/features/Migration/Start.tsx +106 -0
  59. package/src/app/chat/features/Migration/UpgradeButton.tsx +74 -0
  60. package/src/app/chat/features/Migration/const.ts +15 -0
  61. package/src/app/chat/features/Migration/index.tsx +44 -0
  62. package/src/app/chat/features/SessionListContent/DefaultMode.tsx +54 -0
  63. package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +5 -4
  64. package/src/app/chat/features/SessionListContent/List/Item/index.tsx +22 -28
  65. package/src/app/chat/features/SessionListContent/SearchMode.tsx +15 -0
  66. package/src/app/chat/features/SessionListContent/index.tsx +4 -44
  67. package/src/app/chat/features/SessionSearchBar/index.tsx +11 -3
  68. package/src/app/chat/features/TopicListContent/Header.tsx +4 -4
  69. package/src/app/chat/features/TopicListContent/Topic/TopicContent.tsx +11 -9
  70. package/src/app/chat/features/TopicListContent/Topic/TopicItem.tsx +15 -13
  71. package/src/app/chat/features/TopicListContent/Topic/index.tsx +9 -16
  72. package/src/app/chat/features/TopicListContent/TopicSearchBar/index.tsx +13 -4
  73. package/src/app/chat/features/TopicListContent/index.tsx +1 -2
  74. package/src/app/chat/page.tsx +6 -1
  75. package/src/app/chat/settings/features/HeaderContent.tsx +3 -3
  76. package/src/app/home/Redirect.tsx +13 -7
  77. package/src/app/settings/(mobile)/features/ExtraList.tsx +12 -4
  78. package/src/app/settings/common/Common.tsx +16 -4
  79. package/src/app/welcome/features/Banner/index.tsx +9 -10
  80. package/src/chains/summaryTitle.ts +5 -2
  81. package/src/components/DataStyleModal/index.tsx +64 -0
  82. package/src/config/client.ts +5 -0
  83. package/src/const/session.ts +21 -0
  84. package/src/const/settings.ts +1 -1
  85. package/src/database/core/__tests__/db.test.ts +8 -5
  86. package/src/database/core/__tests__/model.test.ts +3 -3
  87. package/src/database/core/db.ts +38 -10
  88. package/src/database/core/index.ts +1 -0
  89. package/src/database/core/model.ts +121 -10
  90. package/src/database/core/schemas.ts +22 -0
  91. package/src/database/core/types/db.ts +13 -0
  92. package/src/database/models/__DEBUG.ts +119 -0
  93. package/src/database/models/{file.test.ts → __tests__/file.test.ts} +19 -6
  94. package/src/database/models/__tests__/message.test.ts +314 -0
  95. package/src/database/models/__tests__/session.test.ts +117 -0
  96. package/src/database/models/__tests__/topic.test.ts +250 -0
  97. package/src/database/models/file.ts +9 -5
  98. package/src/database/models/message.ts +186 -0
  99. package/src/database/models/session.ts +160 -0
  100. package/src/database/models/topic.ts +196 -0
  101. package/src/{types/database → database/schemas}/files.ts +2 -2
  102. package/src/database/schemas/message.ts +37 -0
  103. package/src/database/schemas/session.ts +56 -0
  104. package/src/database/schemas/topic.ts +12 -0
  105. package/src/features/AgentSetting/AgentTTS/SelectWithTTSPreview.tsx +1 -1
  106. package/src/features/AgentSetting/store/action.ts +1 -1
  107. package/src/features/AgentSetting/store/initialState.ts +1 -1
  108. package/src/features/AgentSetting/store/reducers/config.ts +1 -1
  109. package/src/features/DataImporter/index.tsx +240 -0
  110. package/src/features/DebugUI/index.tsx +34 -0
  111. package/src/features/SideBar/BottomActions.tsx +8 -14
  112. package/src/hooks/useImportConfig.ts +13 -30
  113. package/src/hooks/useTTS.ts +1 -1
  114. package/src/layout/GlobalLayout/index.tsx +16 -1
  115. package/src/locales/default/common.ts +19 -0
  116. package/src/locales/default/migration.ts +47 -0
  117. package/src/locales/default/setting.ts +1 -1
  118. package/src/locales/resources/en_US.ts +2 -0
  119. package/src/locales/resources/ja_JP.ts +2 -0
  120. package/src/locales/resources/ko_KR.ts +2 -0
  121. package/src/locales/resources/ru_RU.ts +2 -0
  122. package/src/locales/resources/zh_CN.ts +2 -0
  123. package/src/locales/resources/zh_TW.ts +2 -0
  124. package/src/migrations/FromV0ToV1.ts +2 -4
  125. package/src/migrations/FromV1ToV2/fixtures/input-v1-session.json +191 -0
  126. package/src/migrations/FromV1ToV2/fixtures/output-v2.json +202 -0
  127. package/src/migrations/FromV1ToV2/index.ts +82 -0
  128. package/src/migrations/FromV1ToV2/migrations.test.ts +224 -0
  129. package/src/migrations/FromV1ToV2/types/v1.ts +78 -0
  130. package/src/migrations/FromV1ToV2/types/v2.ts +52 -0
  131. package/src/{utils → migrations}/VersionController.test.ts +1 -3
  132. package/src/{utils → migrations}/VersionController.ts +5 -2
  133. package/src/migrations/index.ts +17 -3
  134. package/src/services/__tests__/file.test.ts +3 -3
  135. package/src/services/chat.ts +66 -5
  136. package/src/services/config.ts +184 -0
  137. package/src/services/debug.ts +13 -0
  138. package/src/services/file.ts +6 -2
  139. package/src/services/github.ts +25 -0
  140. package/src/services/message.ts +79 -0
  141. package/src/services/session.ts +63 -0
  142. package/src/services/topic.ts +56 -0
  143. package/src/store/chat/actions/enhance.ts +95 -0
  144. package/src/store/{session/slices/chat → chat}/actions/message.ts +225 -247
  145. package/src/store/{session/slices/chat → chat}/actions/plugin.ts +29 -34
  146. package/src/store/{session/slices/chat → chat}/actions/share.ts +9 -13
  147. package/src/store/chat/actions/topic.ts +197 -0
  148. package/src/store/chat/helpers.ts +23 -0
  149. package/src/store/chat/index.ts +3 -0
  150. package/src/store/chat/initialState.ts +48 -0
  151. package/src/store/chat/reducers/message.test.ts +83 -0
  152. package/src/store/chat/reducers/message.ts +83 -0
  153. package/src/store/chat/selectors/__tests__/topic.test.ts +43 -0
  154. package/src/store/chat/selectors/chat.ts +119 -0
  155. package/src/store/chat/selectors/index.ts +2 -0
  156. package/src/store/chat/selectors/topic.ts +28 -0
  157. package/src/store/chat/store.ts +63 -0
  158. package/src/store/files/slices/images/action.ts +6 -4
  159. package/src/store/global/store.ts +1 -1
  160. package/src/store/middleware/createHyperStorage/index.ts +40 -24
  161. package/src/store/middleware/createHyperStorage/keyMapper.ts +8 -2
  162. package/src/store/middleware/createHyperStorage/type.ts +10 -8
  163. package/src/store/plugin/slices/plugin/action.ts +1 -1
  164. package/src/store/plugin/store.ts +2 -4
  165. package/src/store/session/helpers.ts +1 -1
  166. package/src/store/session/hooks/useSessionHydrated.ts +6 -10
  167. package/src/store/session/initialState.ts +26 -7
  168. package/src/store/session/selectors.ts +1 -2
  169. package/src/store/session/slices/agent/action.ts +26 -35
  170. package/src/store/session/slices/agent/selectors.ts +32 -37
  171. package/src/store/session/slices/session/action.ts +81 -88
  172. package/src/store/session/slices/session/helpers.ts +19 -0
  173. package/src/store/session/slices/session/initialState.ts +7 -10
  174. package/src/store/session/slices/session/selectors/export.ts +12 -9
  175. package/src/store/session/slices/session/selectors/index.ts +2 -35
  176. package/src/store/session/slices/session/selectors/list.test.ts +25 -203
  177. package/src/store/session/slices/session/selectors/list.ts +40 -78
  178. package/src/store/session/store.ts +11 -17
  179. package/src/types/agent/index.ts +61 -0
  180. package/src/types/chatMessage.ts +11 -20
  181. package/src/types/exportConfig.ts +7 -2
  182. package/src/types/files.ts +2 -2
  183. package/src/types/llm.ts +1 -1
  184. package/src/types/meta.ts +24 -12
  185. package/src/types/openai/chat.ts +9 -1
  186. package/src/types/openai/functionCall.ts +4 -0
  187. package/src/types/session.ts +10 -94
  188. package/src/types/translate.ts +2 -1
  189. package/src/utils/config.ts +5 -11
  190. package/src/utils/fetch.ts +8 -2
  191. package/src/app/chat/features/Conversation/usePluginsInit.ts +0 -14
  192. package/src/database/core/schema.ts +0 -9
  193. package/src/helpers/export.ts +0 -69
  194. package/src/hooks/useExportConfig.ts +0 -6
  195. package/src/store/session/slices/chat/actions/index.ts +0 -32
  196. package/src/store/session/slices/chat/actions/topic.ts +0 -171
  197. package/src/store/session/slices/chat/actions/translate.ts +0 -113
  198. package/src/store/session/slices/chat/helpers.ts +0 -9
  199. package/src/store/session/slices/chat/initialState.ts +0 -13
  200. package/src/store/session/slices/chat/reducers/message.test.ts +0 -319
  201. package/src/store/session/slices/chat/reducers/message.ts +0 -156
  202. package/src/store/session/slices/chat/reducers/topic.test.ts +0 -179
  203. package/src/store/session/slices/chat/reducers/topic.ts +0 -81
  204. package/src/store/session/slices/chat/selectors/chat.ts +0 -111
  205. package/src/store/session/slices/chat/selectors/index.ts +0 -26
  206. package/src/store/session/slices/chat/selectors/topic.ts +0 -30
  207. package/src/store/session/slices/chat/selectors/utils.test.ts +0 -385
  208. package/src/store/session/slices/chat/selectors/utils.ts +0 -91
  209. package/src/store/session/slices/chat/utils.ts +0 -13
  210. package/src/store/session/slices/session/reducers/session.test.ts +0 -577
  211. package/src/store/session/slices/session/reducers/session.ts +0 -182
  212. package/src/types/database/db.ts +0 -4
  213. /package/src/store/{session/slices/chat → chat}/reducers/files.test.ts +0 -0
  214. /package/src/store/{session/slices/chat → chat}/reducers/files.ts +0 -0
@@ -0,0 +1,119 @@
1
+ import { LobePluginType } from '@lobehub/chat-plugin-sdk';
2
+ import { t } from 'i18next';
3
+
4
+ import { DEFAULT_INBOX_AVATAR, DEFAULT_USER_AVATAR } from '@/const/meta';
5
+ import { INBOX_SESSION_ID } from '@/const/session';
6
+ import { chatHelpers } from '@/store/chat/helpers';
7
+ import { useGlobalStore } from '@/store/global';
8
+ import { useSessionStore } from '@/store/session';
9
+ import { agentSelectors } from '@/store/session/selectors';
10
+ import { ChatMessage } from '@/types/chatMessage';
11
+ import { merge } from '@/utils/merge';
12
+
13
+ import type { ChatStore } from '../store';
14
+
15
+ const getMeta = (message: ChatMessage) => {
16
+ switch (message.role) {
17
+ case 'user': {
18
+ return {
19
+ avatar: useGlobalStore.getState().settings.avatar || DEFAULT_USER_AVATAR,
20
+ };
21
+ }
22
+
23
+ case 'system': {
24
+ return message.meta;
25
+ }
26
+
27
+ case 'assistant': {
28
+ return agentSelectors.currentAgentMeta(useSessionStore.getState());
29
+ }
30
+
31
+ case 'function': {
32
+ // TODO: 后续改成将 plugin metadata 写入 message metadata 的方案
33
+ return {
34
+ avatar: '🧩',
35
+ title: 'plugin-unknown',
36
+ };
37
+ }
38
+ }
39
+ };
40
+
41
+ // 当前激活的消息列表
42
+ const currentChats = (s: ChatStore): ChatMessage[] => {
43
+ if (!s.activeId) return [];
44
+
45
+ return s.messages.map((i) => ({ ...i, meta: getMeta(i) }));
46
+ };
47
+
48
+ const initTime = Date.now();
49
+ // 针对新助手添加初始化时的自定义消息
50
+ const currentChatsWithGuideMessage = (s: ChatStore): ChatMessage[] => {
51
+ const data = currentChats(s);
52
+
53
+ const isBrandNewChat = data.length === 0;
54
+
55
+ if (!isBrandNewChat) return data;
56
+
57
+ const [activeId, isInbox] = [s.activeId, s.activeId === INBOX_SESSION_ID];
58
+ const meta = agentSelectors.currentAgentMeta(useSessionStore.getState());
59
+
60
+ const inboxMsg = t('inbox.defaultMessage', { ns: 'chat' });
61
+ const agentSystemRoleMsg = t('agentDefaultMessageWithSystemRole', {
62
+ name: meta.title || t('defaultAgent'),
63
+ ns: 'chat',
64
+ systemRole: meta.description,
65
+ });
66
+ const agentMsg = t('agentDefaultMessage', {
67
+ id: activeId,
68
+ name: meta.title || t('defaultAgent'),
69
+ ns: 'chat',
70
+ });
71
+
72
+ const emptyInboxGuideMessage = {
73
+ content: isInbox ? inboxMsg : !!meta.description ? agentSystemRoleMsg : agentMsg,
74
+ createdAt: initTime,
75
+ extra: {},
76
+ id: 'default',
77
+ meta: merge({ avatar: DEFAULT_INBOX_AVATAR }, meta),
78
+ role: 'assistant',
79
+ updatedAt: initTime,
80
+ } as ChatMessage;
81
+
82
+ return [emptyInboxGuideMessage];
83
+ };
84
+
85
+ const currentChatsWithHistoryConfig = (s: ChatStore): ChatMessage[] => {
86
+ const chats = currentChats(s);
87
+ const config = agentSelectors.currentAgentConfig(useSessionStore.getState());
88
+
89
+ return chatHelpers.getSlicedMessagesWithConfig(chats, config);
90
+ };
91
+
92
+ const chatsMessageString = (s: ChatStore): string => {
93
+ const chats = currentChatsWithHistoryConfig(s);
94
+ return chats.map((m) => m.content).join('');
95
+ };
96
+
97
+ const getFunctionMessageProps =
98
+ ({ plugin, content, id }: Pick<ChatMessage, 'plugin' | 'content' | 'id'>) =>
99
+ (s: ChatStore) => ({
100
+ arguments: plugin?.arguments,
101
+ command: plugin,
102
+ content,
103
+ id: plugin?.identifier,
104
+ loading: id === s.chatLoadingId,
105
+ type: plugin?.type as LobePluginType,
106
+ });
107
+
108
+ const getMessageById = (id: string) => (s: ChatStore) => {
109
+ return s.messages.find((m) => m.id === id);
110
+ };
111
+
112
+ export const chatSelectors = {
113
+ chatsMessageString,
114
+ currentChats,
115
+ currentChatsWithGuideMessage,
116
+ currentChatsWithHistoryConfig,
117
+ getFunctionMessageProps,
118
+ getMessageById,
119
+ };
@@ -0,0 +1,2 @@
1
+ export { chatSelectors } from './chat';
2
+ export { topicSelectors } from './topic';
@@ -0,0 +1,28 @@
1
+ import { ChatTopic } from '@/types/topic';
2
+
3
+ import { ChatStore } from '../store';
4
+
5
+ const currentTopics = (s: ChatStore): ChatTopic[] => s.topics;
6
+
7
+ const searchTopics = (s: ChatStore): ChatTopic[] => s.searchTopics;
8
+
9
+ const displayTopics = (s: ChatStore): ChatTopic[] =>
10
+ s.isSearchingTopic ? searchTopics(s) : currentTopics(s);
11
+
12
+ const currentUnFavTopics = (s: ChatStore): ChatTopic[] => s.topics.filter((s) => !s.favorite);
13
+
14
+ const currentTopicLength = (s: ChatStore): number => currentTopics(s).length;
15
+
16
+ const getTopicById =
17
+ (id: string) =>
18
+ (s: ChatStore): ChatTopic | undefined =>
19
+ currentTopics(s).find((topic) => topic.id === id);
20
+
21
+ export const topicSelectors = {
22
+ currentTopicLength,
23
+ currentTopics,
24
+ currentUnFavTopics,
25
+ displayTopics,
26
+ getTopicById,
27
+ searchTopics,
28
+ };
@@ -0,0 +1,63 @@
1
+ import { PersistOptions, devtools, persist } from 'zustand/middleware';
2
+ import { shallow } from 'zustand/shallow';
3
+ import { createWithEqualityFn } from 'zustand/traditional';
4
+ import { StateCreator } from 'zustand/vanilla';
5
+
6
+ import { createHyperStorage } from '@/store/middleware/createHyperStorage';
7
+ import { isDev } from '@/utils/env';
8
+
9
+ import { ChatEnhanceAction, chatEnhance } from './actions/enhance';
10
+ import { ChatMessageAction, chatMessage } from './actions/message';
11
+ import { ChatPluginAction, chatPlugin } from './actions/plugin';
12
+ import { ShareAction, chatShare } from './actions/share';
13
+ import { ChatTopicAction, chatTopic } from './actions/topic';
14
+ import { ChatStoreState, initialState } from './initialState';
15
+
16
+ export interface ChatStoreAction
17
+ extends ChatMessageAction,
18
+ ChatTopicAction,
19
+ ShareAction,
20
+ ChatEnhanceAction,
21
+ ChatPluginAction {}
22
+
23
+ export type ChatStore = ChatStoreAction & ChatStoreState;
24
+
25
+ // =============== 聚合 createStoreFn ============ //
26
+
27
+ const createStore: StateCreator<ChatStore, [['zustand/devtools', never]]> = (...params) => ({
28
+ ...initialState,
29
+
30
+ ...chatMessage(...params),
31
+ ...chatTopic(...params),
32
+ ...chatShare(...params),
33
+ ...chatEnhance(...params),
34
+ ...chatPlugin(...params),
35
+ });
36
+
37
+ const persistOptions: PersistOptions<ChatStore> = {
38
+ name: 'LobeChat_Chat',
39
+
40
+ // 手动控制 Hydration ,避免 ssr 报错
41
+ skipHydration: true,
42
+
43
+ storage: createHyperStorage({
44
+ localStorage: false,
45
+ url: {
46
+ mode: 'hash',
47
+ selectors: [{ activeTopicId: 'topic' }],
48
+ },
49
+ }),
50
+ version: 0,
51
+ };
52
+
53
+ // =============== 实装 useStore ============ //
54
+
55
+ export const useChatStore = createWithEqualityFn<ChatStore>()(
56
+ persist(
57
+ devtools(createStore, {
58
+ name: 'LobeChat_Chat' + (isDev ? '_DEV' : ''),
59
+ }),
60
+ persistOptions,
61
+ ),
62
+ shallow,
63
+ );
@@ -8,14 +8,13 @@ import { setNamespace } from '@/utils/storeDebug';
8
8
 
9
9
  import { FileStore } from '../../store';
10
10
 
11
- const t = setNamespace('files/image');
11
+ const t = setNamespace('image');
12
12
 
13
- /**
14
- * 代理行为接口
15
- */
16
13
  export interface FileAction {
17
14
  clearImageList: () => void;
15
+ removeAllFiles: () => Promise<void>;
18
16
  removeFile: (id: string) => Promise<void>;
17
+
19
18
  setImageMapItem: (id: string, item: FilePreview) => void;
20
19
  uploadFile: (file: File) => Promise<void>;
21
20
 
@@ -31,6 +30,9 @@ export const createFileSlice: StateCreator<
31
30
  clearImageList: () => {
32
31
  set({ inputFilesList: [] }, false, t('clearImageList'));
33
32
  },
33
+ removeAllFiles: async () => {
34
+ await fileService.removeAllFiles();
35
+ },
34
36
  removeFile: async (id) => {
35
37
  await fileService.removeFile(id);
36
38
 
@@ -77,7 +77,7 @@ const persistOptions: PersistOptions<GlobalStore, GlobalPersist> = {
77
77
  export const useGlobalStore = createWithEqualityFn<GlobalStore>()(
78
78
  persist(
79
79
  devtools(createStore, {
80
- name: 'LOBE_GLOBAL' + (isDev ? '_DEV' : ''),
80
+ name: 'LobeChat_Global' + (isDev ? '_DEV' : ''),
81
81
  }),
82
82
  persistOptions,
83
83
  ),
@@ -10,9 +10,20 @@ import { creatUrlStorage } from './urlStorage';
10
10
  export const createHyperStorage = <T extends object>(
11
11
  options: HyperStorageOptions,
12
12
  ): PersistStorage<T> => {
13
+ const getLocalStorageConfig = () => {
14
+ if (options.localStorage === false) {
15
+ return { dbName: '', skipLocalStorage: true, useIndexedDB: false };
16
+ }
17
+
18
+ const useIndexedDB = options.localStorage?.mode === 'indexedDB';
19
+ const dbName = options.localStorage?.dbName || 'indexedDB';
20
+
21
+ return { dbName, skipLocalStorage: false, useIndexedDB };
22
+ };
23
+
13
24
  const hasUrl = !!options.url;
14
- const useIndexedDB = options.localStorage?.mode === 'indexedDB';
15
- const dbName = options.localStorage?.dbName || 'indexedDB';
25
+
26
+ const { skipLocalStorage, useIndexedDB, dbName } = getLocalStorageConfig();
16
27
 
17
28
  const { mapStateKeyToStorageKey, getStateKeyFromStorageKey } = createKeyMapper(options);
18
29
 
@@ -25,20 +36,22 @@ export const createHyperStorage = <T extends object>(
25
36
  let version: number | undefined;
26
37
 
27
38
  // ============== 处理 Local Storage ============== //
28
- let localState: StorageValue<T> | undefined;
39
+ if (!skipLocalStorage) {
40
+ let localState: StorageValue<T> | undefined;
29
41
 
30
- // 如果使用 indexedDB,优先从 indexedDB 中获取
31
- if (useIndexedDB) {
32
- localState = await indexedDB.getItem(name);
33
- }
34
- // 如果 indexedDB 中不存在,则再试试 localStorage
35
- if (!localState) localState = localStorage.getItem(name);
36
-
37
- if (localState) {
38
- version = localState.version;
39
- for (const [k, v] of Object.entries(localState.state)) {
40
- const key = getStateKeyFromStorageKey(k, 'localStorage');
41
- if (key) state[key] = v;
42
+ // 如果使用 indexedDB,优先从 indexedDB 中获取
43
+ if (useIndexedDB) {
44
+ localState = await indexedDB.getItem(name);
45
+ }
46
+ // 如果 indexedDB 中不存在,则再试试 localStorage
47
+ if (!localState) localState = localStorage.getItem(name);
48
+
49
+ if (localState) {
50
+ version = localState.version;
51
+ for (const [k, v] of Object.entries(localState.state)) {
52
+ const key = getStateKeyFromStorageKey(k, 'localStorage');
53
+ if (key) state[key] = v;
54
+ }
42
55
  }
43
56
  }
44
57
 
@@ -62,11 +75,12 @@ export const createHyperStorage = <T extends object>(
62
75
  },
63
76
  removeItem: async (key) => {
64
77
  // ============== 处理 Local Storage ============== //
65
-
66
- if (useIndexedDB) {
67
- await indexedDB.removeItem(key);
68
- } else {
69
- localStorage.removeItem(key);
78
+ if (!skipLocalStorage) {
79
+ if (useIndexedDB) {
80
+ await indexedDB.removeItem(key);
81
+ } else {
82
+ localStorage.removeItem(key);
83
+ }
70
84
  }
71
85
 
72
86
  // ============== 处理 URL Storage ============== //
@@ -85,10 +99,12 @@ export const createHyperStorage = <T extends object>(
85
99
  if (localKey) localState[localKey] = v;
86
100
  }
87
101
 
88
- if (useIndexedDB) {
89
- await indexedDB.setItem(name, localState, newValue.version);
90
- } else {
91
- localStorage.setItem(name, localState, newValue.version);
102
+ if (!skipLocalStorage) {
103
+ if (useIndexedDB) {
104
+ await indexedDB.setItem(name, localState, newValue.version);
105
+ } else {
106
+ localStorage.setItem(name, localState, newValue.version);
107
+ }
92
108
  }
93
109
 
94
110
  // ============== 处理 URL Storage ============== //
@@ -5,7 +5,10 @@ export const createKeyMapper = (options: HyperStorageOptions) => {
5
5
  key: string,
6
6
  mode: keyof HyperStorageOptions = 'localStorage',
7
7
  ) => {
8
- const selectors = options[mode]?.selectors;
8
+ const media = options[mode];
9
+ if (media === false) return key;
10
+
11
+ const selectors = media?.selectors;
9
12
  if (!selectors) return key;
10
13
 
11
14
  let storageKey: string | undefined;
@@ -27,7 +30,10 @@ export const createKeyMapper = (options: HyperStorageOptions) => {
27
30
  key: string,
28
31
  mode: keyof HyperStorageOptions = 'localStorage',
29
32
  ) => {
30
- const selectors = options[mode]?.selectors;
33
+ const media = options[mode];
34
+ if (media === false) return key;
35
+
36
+ const selectors = media?.selectors;
31
37
  if (!selectors) return key;
32
38
 
33
39
  let stateKey: string | undefined;
@@ -1,14 +1,16 @@
1
1
  export type StorageSelector = string | Record<string, string>;
2
2
 
3
+ export interface LocalStorageOptions {
4
+ dbName?: string;
5
+ /**
6
+ * @default 'localStorage'
7
+ */
8
+ mode?: 'indexedDB' | 'localStorage';
9
+ selectors: StorageSelector[];
10
+ }
11
+
3
12
  export interface HyperStorageOptions {
4
- localStorage?: {
5
- dbName?: string;
6
- /**
7
- * @default 'localStorage'
8
- */
9
- mode?: 'indexedDB' | 'localStorage';
10
- selectors: StorageSelector[];
11
- };
13
+ localStorage?: LocalStorageOptions | false;
12
14
  url?: {
13
15
  /**
14
16
  * @default 'hash'
@@ -48,7 +48,7 @@ export const createPluginSlice: StateCreator<
48
48
 
49
49
  let enabledPlugins: string[] = [];
50
50
 
51
- for (const session of Object.values(sessions)) {
51
+ for (const session of sessions) {
52
52
  const plugins = session.config.plugins;
53
53
  if (!plugins || plugins.length === 0) continue;
54
54
 
@@ -26,10 +26,8 @@ type SessionPersist = Pick<
26
26
  'pluginList' | 'pluginManifestMap' | 'pluginsSettings' | 'customPluginList'
27
27
  >;
28
28
 
29
- const storeName = 'LOBE_PLUGIN';
30
-
31
29
  const persistOptions: PersistOptions<PluginStore, SessionPersist> = {
32
- name: storeName,
30
+ name: 'LOBE_PLUGIN',
33
31
 
34
32
  partialize: (s) => ({
35
33
  customPluginList: s.customPluginList,
@@ -48,7 +46,7 @@ const persistOptions: PersistOptions<PluginStore, SessionPersist> = {
48
46
  export const usePluginStore = createWithEqualityFn<PluginStore>()(
49
47
  persist(
50
48
  devtools(createStore, {
51
- name: storeName + (isDev ? '_DEV' : ''),
49
+ name: 'LobeChat_Plugin' + (isDev ? '_DEV' : ''),
52
50
  }),
53
51
  persistOptions,
54
52
  ),
@@ -1 +1 @@
1
- export * from './slices/chat/helpers';
1
+ export * from './slices/session/helpers';
@@ -1,17 +1,13 @@
1
- import { useState } from 'react';
2
-
3
- import { sessionSelectors } from '../selectors';
4
1
  import { useSessionStore } from '../store';
5
- import { useEffectAfterSessionHydrated } from './useEffectAfterHydrated';
6
2
 
7
3
  export const useSessionHydrated = () => {
8
- const hasInited = sessionSelectors.hasConversion(useSessionStore.getState());
4
+ const [fetchSessionsLoading, useFetchSessions] = useSessionStore((s) => [
5
+ s.fetchSessionsLoading,
9
6
 
10
- const [isInit, setInit] = useState(hasInited);
7
+ s.useFetchSessions,
8
+ ]);
11
9
 
12
- useEffectAfterSessionHydrated(() => {
13
- if (!isInit) setInit(true);
14
- }, []);
10
+ const { isLoading } = useFetchSessions();
15
11
 
16
- return isInit;
12
+ return !(isLoading || fetchSessionsLoading);
17
13
  };
@@ -1,11 +1,30 @@
1
- import { ChatState, initialChatState } from './slices/chat/initialState';
2
- import { SessionState, initialSessionState } from './slices/session/initialState';
1
+ import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
3
2
 
4
- export type SessionStoreState = SessionState & ChatState;
3
+ import { LobeAgentSession } from '@/types/session';
4
+
5
+ export type SessionStoreState = SessionState;
6
+
7
+ export interface SessionState {
8
+ /**
9
+ * @title 当前活动的会话
10
+ * @description 当前正在编辑或查看的会话
11
+ */
12
+ activeId: string;
13
+ fetchSessionsLoading: boolean;
14
+ isMobile?: boolean;
15
+ isSearching: boolean;
16
+ router?: AppRouterInstance;
17
+ searchKeywords: string;
18
+ searchSessions: LobeAgentSession[];
19
+ sessions: LobeAgentSession[];
20
+ }
5
21
 
6
22
  export const initialState: SessionStoreState = {
7
- ...initialSessionState,
8
- ...initialChatState,
23
+ activeId: 'inbox',
24
+ fetchSessionsLoading: true,
25
+ isMobile: false,
26
+ isSearching: false,
27
+ searchKeywords: '',
28
+ searchSessions: [],
29
+ sessions: [],
9
30
  };
10
-
11
- export { initLobeSession } from './slices/session/initialState';
@@ -1,3 +1,2 @@
1
1
  export { agentSelectors } from './slices/agent/selectors';
2
- export { chatSelectors, topicSelectors } from './slices/chat/selectors';
3
- export { sessionSelectors } from './slices/session/selectors';
2
+ export { sessionExportSelectors,sessionSelectors } from './slices/session/selectors';
@@ -1,9 +1,10 @@
1
1
  import { produce } from 'immer';
2
2
  import { StateCreator } from 'zustand/vanilla';
3
3
 
4
+ import { sessionService } from '@/services/session';
4
5
  import { useGlobalStore } from '@/store/global';
6
+ import { LobeAgentConfig } from '@/types/agent';
5
7
  import { MetaData } from '@/types/meta';
6
- import { LobeAgentConfig } from '@/types/session';
7
8
 
8
9
  import { SessionStore } from '../../store';
9
10
  import { sessionSelectors } from '../session/selectors';
@@ -13,10 +14,6 @@ import { sessionSelectors } from '../session/selectors';
13
14
  */
14
15
  export interface AgentAction {
15
16
  removePlugin: (id: string) => void;
16
- /**
17
- * 更新代理配置
18
- * @param config - 部分 LobeAgentConfig 的配置
19
- */
20
17
  updateAgentConfig: (config: Partial<LobeAgentConfig>) => void;
21
18
  updateAgentMeta: (meta: Partial<MetaData>) => void;
22
19
  }
@@ -27,51 +24,45 @@ export const createAgentSlice: StateCreator<
27
24
  [],
28
25
  AgentAction
29
26
  > = (set, get) => ({
30
- removePlugin: (id) => {
31
- const { activeId } = get();
27
+ removePlugin: async (id) => {
32
28
  const session = sessionSelectors.currentSession(get());
33
- if (!activeId || !session) return;
29
+ if (!session) return;
30
+
31
+ const { activeId, refreshSessions } = get();
34
32
 
35
33
  const config = produce(session.config, (draft) => {
36
34
  draft.plugins = draft.plugins?.filter((i) => i !== id) || [];
37
35
  });
38
36
 
39
- get().dispatchSession({ config, id: activeId, type: 'updateSessionConfig' });
37
+ await sessionService.updateSessionConfig(activeId, config);
38
+ await refreshSessions();
40
39
  },
41
40
 
42
- updateAgentConfig: (config) => {
43
- const { activeId, dispatchSession } = get();
44
- const session = sessionSelectors.currentSession(get());
45
- if (!activeId || !session) return;
46
-
41
+ updateAgentConfig: async (config) => {
47
42
  // if is the inbox session, update the global config
48
- if (sessionSelectors.isInboxSession(get())) {
43
+ const isInbox = sessionSelectors.isInboxSession(get());
44
+ if (isInbox) {
49
45
  useGlobalStore.getState().updateDefaultAgent({ config });
50
- // NOTE: DON'T ADD RETURN HERE.
51
- // we need to use `dispatchSession` below to update inbox config to trigger the inbox config rerender
46
+ } else {
47
+ const session = sessionSelectors.currentSession(get());
48
+ if (!session) return;
49
+
50
+ const { activeId } = get();
51
+
52
+ await sessionService.updateSessionConfig(activeId, config);
52
53
  }
53
54
 
54
- // Although we use global store to store the default agent config,
55
- // due to the `currentAgentConfig` selector use `useGlobalStore.getState()`
56
- // to get the default agent config which not rerender on global store update
57
- // we need to update the session config here.
58
- dispatchSession({ config, id: activeId, type: 'updateSessionConfig' });
55
+ // trigger store rerender
56
+ await get().refreshSessions();
59
57
  },
60
58
 
61
- updateAgentMeta: (meta) => {
62
- const { activeId } = get();
59
+ updateAgentMeta: async (meta) => {
63
60
  const session = sessionSelectors.currentSession(get());
64
- if (!activeId || !session) return;
61
+ if (!session) return;
65
62
 
66
- for (const [key, value] of Object.entries(meta)) {
67
- if (value !== undefined) {
68
- get().dispatchSession({
69
- id: activeId,
70
- key: key as keyof MetaData,
71
- type: 'updateSessionMeta',
72
- value,
73
- });
74
- }
75
- }
63
+ const { activeId, refreshSessions } = get();
64
+
65
+ await sessionService.updateSessionMeta(activeId, meta);
66
+ await refreshSessions();
76
67
  },
77
68
  });