@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
@@ -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
 
@@ -1,5 +1,104 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
+ exports[`settingsSelectors > CUSTOM_MODELS > custom deletion, addition, and renaming of models 1`] = `
4
+ [
5
+ {
6
+ "displayName": "llama",
7
+ "name": "llama",
8
+ },
9
+ {
10
+ "displayName": "claude-2",
11
+ "name": "claude-2",
12
+ },
13
+ {
14
+ "displayName": "gpt-4-32k",
15
+ "name": "gpt-4-1106-preview",
16
+ },
17
+ ]
18
+ `;
19
+
20
+ exports[`settingsSelectors > CUSTOM_MODELS > duplicate naming model 1`] = `
21
+ [
22
+ {
23
+ "displayName": "gpt-3.5-turbo",
24
+ "name": "gpt-3.5-turbo",
25
+ },
26
+ {
27
+ "displayName": "gpt-3.5-turbo-1106",
28
+ "name": "gpt-3.5-turbo-1106",
29
+ },
30
+ {
31
+ "displayName": "gpt-3.5-turbo-16k",
32
+ "name": "gpt-3.5-turbo-16k",
33
+ },
34
+ {
35
+ "displayName": "gpt-4",
36
+ "name": "gpt-4",
37
+ },
38
+ {
39
+ "displayName": "gpt-4-32k",
40
+ "name": "gpt-4-32k",
41
+ },
42
+ {
43
+ "displayName": "gpt-4-vision-preview",
44
+ "name": "gpt-4-vision-preview",
45
+ },
46
+ {
47
+ "displayName": "gpt-4-32k",
48
+ "name": "gpt-4-1106-preview",
49
+ },
50
+ ]
51
+ `;
52
+
53
+ exports[`settingsSelectors > CUSTOM_MODELS > only add the model 1`] = `
54
+ [
55
+ {
56
+ "displayName": "gpt-3.5-turbo",
57
+ "name": "gpt-3.5-turbo",
58
+ },
59
+ {
60
+ "displayName": "gpt-3.5-turbo-1106",
61
+ "name": "gpt-3.5-turbo-1106",
62
+ },
63
+ {
64
+ "displayName": "gpt-3.5-turbo-16k",
65
+ "name": "gpt-3.5-turbo-16k",
66
+ },
67
+ {
68
+ "displayName": "gpt-4",
69
+ "name": "gpt-4",
70
+ },
71
+ {
72
+ "displayName": "gpt-4-32k",
73
+ "name": "gpt-4-32k",
74
+ },
75
+ {
76
+ "displayName": "gpt-4-1106-preview",
77
+ "name": "gpt-4-1106-preview",
78
+ },
79
+ {
80
+ "displayName": "gpt-4-vision-preview",
81
+ "name": "gpt-4-vision-preview",
82
+ },
83
+ {
84
+ "displayName": "model1",
85
+ "name": "model1",
86
+ },
87
+ {
88
+ "displayName": "model2",
89
+ "name": "model2",
90
+ },
91
+ {
92
+ "displayName": "model3",
93
+ "name": "model3",
94
+ },
95
+ {
96
+ "displayName": "model4",
97
+ "name": "model4",
98
+ },
99
+ ]
100
+ `;
101
+
3
102
  exports[`settingsSelectors > currentSettings > should merge DEFAULT_SETTINGS and s.settings correctly 1`] = `
4
103
  {
5
104
  "avatar": "avatar.jpg",
@@ -58,6 +58,57 @@ describe('settingsSelectors', () => {
58
58
  });
59
59
  });
60
60
 
61
+ describe('CUSTOM_MODELS', () => {
62
+ it('custom deletion, addition, and renaming of models', () => {
63
+ const s = {
64
+ settings: {
65
+ languageModel: {
66
+ openAI: {
67
+ customModelName:
68
+ '-all,+llama,+claude-2,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k',
69
+ },
70
+ },
71
+ },
72
+ } as unknown as GlobalStore;
73
+
74
+ const result = settingsSelectors.modelList(s);
75
+
76
+ expect(result).toMatchSnapshot();
77
+ });
78
+
79
+ it('duplicate naming model', () => {
80
+ const s = {
81
+ settings: {
82
+ languageModel: {
83
+ openAI: {
84
+ customModelName: 'gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k',
85
+ },
86
+ },
87
+ },
88
+ } as unknown as GlobalStore;
89
+
90
+ const result = settingsSelectors.modelList(s);
91
+
92
+ expect(result).toMatchSnapshot();
93
+ });
94
+
95
+ it('only add the model', () => {
96
+ const s = {
97
+ settings: {
98
+ languageModel: {
99
+ openAI: {
100
+ customModelName: 'model1,model2,model3,model4',
101
+ },
102
+ },
103
+ },
104
+ } as unknown as GlobalStore;
105
+
106
+ const result = settingsSelectors.modelList(s);
107
+
108
+ expect(result).toMatchSnapshot();
109
+ });
110
+ });
111
+
61
112
  // describe('defaultAgent', () => {
62
113
  // it('should merge DEFAULT_AGENT and s.settings.defaultAgent correctly', () => {
63
114
  // const s: GlobalStore = {
@@ -8,7 +8,7 @@ import {
8
8
  DEFAULT_TTS_CONFIG,
9
9
  } from '@/const/settings';
10
10
  import { Locales } from '@/locales/resources';
11
- import { GlobalSettings } from '@/types/settings';
11
+ import { CustomModels, GlobalSettings } from '@/types/settings';
12
12
  import { isOnServerSide } from '@/utils/env';
13
13
  import { merge } from '@/utils/merge';
14
14
 
@@ -29,10 +29,41 @@ const openAIAPIKeySelectors = (s: GlobalStore) =>
29
29
 
30
30
  const openAIProxyUrlSelectors = (s: GlobalStore) => s.settings.languageModel.openAI.endpoint;
31
31
 
32
- const modelListSelectors = (s: GlobalStore) => [
33
- ...DEFAULT_OPENAI_MODEL_LIST,
34
- ...(s.settings.languageModel.openAI.customModelName || '').split(',').filter(Boolean),
35
- ];
32
+ const modelListSelectors = (s: GlobalStore) => {
33
+ let models: CustomModels = [];
34
+
35
+ const modelNames = [
36
+ ...DEFAULT_OPENAI_MODEL_LIST,
37
+ ...(s.settings.languageModel.openAI.customModelName || '').split(/[,,]/).filter(Boolean),
38
+ ];
39
+
40
+ for (const item of modelNames) {
41
+ const disable = item.startsWith('-');
42
+ const nameConfig = item.startsWith('+') || item.startsWith('-') ? item.slice(1) : item;
43
+ const [name, displayName] = nameConfig.split('=');
44
+
45
+ if (disable) {
46
+ // Disable all models.
47
+ if (name === 'all') {
48
+ models = [];
49
+ }
50
+ continue;
51
+ }
52
+
53
+ // Remove duplicate model entries.
54
+ const existingIndex = models.findIndex(({ name: n }) => n === name);
55
+ if (existingIndex !== -1) {
56
+ models.splice(existingIndex, 1);
57
+ }
58
+
59
+ models.push({
60
+ displayName: displayName || name,
61
+ name,
62
+ });
63
+ }
64
+
65
+ return models;
66
+ };
36
67
 
37
68
  export const exportSettings = (s: GlobalStore) => {
38
69
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -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
  };