@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
@@ -1,113 +0,0 @@
1
- import { produce } from 'immer';
2
- import { StateCreator } from 'zustand/vanilla';
3
-
4
- import { chainLangDetect } from '@/chains/langDetect';
5
- import { chainTranslate } from '@/chains/translate';
6
- import { supportLocales } from '@/locales/options';
7
- import { chatService } from '@/services/chat';
8
- import { SessionStore } from '@/store/session';
9
- import { setNamespace } from '@/utils/storeDebug';
10
-
11
- import { sessionSelectors } from '../../session/selectors';
12
-
13
- const t = setNamespace('chat/translate');
14
-
15
- /**
16
- * 翻译事件
17
- */
18
- export interface ChatTranslateAction {
19
- clearTTS: (id: string) => void;
20
-
21
- clearTranslate: (id: string) => void;
22
- /**
23
- * 翻译消息
24
- * @param id
25
- */
26
- translateMessage: (id: string, targetLang: string) => Promise<void>;
27
- ttsMessage: (id: string, init?: boolean) => void;
28
- }
29
-
30
- export const chatTranslate: StateCreator<
31
- SessionStore,
32
- [['zustand/devtools', never]],
33
- [],
34
- ChatTranslateAction
35
- > = (set, get) => ({
36
- clearTTS: (id) => {
37
- get().dispatchMessage({
38
- id,
39
- key: 'tts',
40
- type: 'updateMessageExtra',
41
- value: null,
42
- });
43
- },
44
-
45
- clearTranslate: (id) => {
46
- get().dispatchMessage({
47
- id,
48
- key: 'translate',
49
- type: 'updateMessageExtra',
50
- value: null,
51
- });
52
- },
53
-
54
- translateMessage: async (id, targetLang) => {
55
- const { toggleChatLoading, dispatchMessage } = get();
56
- const session = sessionSelectors.currentSession(get());
57
- if (!session || !id) return;
58
-
59
- const message = session.chats[id];
60
- if (!message) return;
61
-
62
- let content = '';
63
- let from = '';
64
-
65
- dispatchMessage({
66
- id,
67
- key: 'translate',
68
- type: 'updateMessageExtra',
69
- value: { content: '', to: targetLang },
70
- });
71
-
72
- toggleChatLoading(true, id, t('translateMessage(start)', { id }) as string);
73
-
74
- // detect from language
75
- chatService
76
- .fetchPresetTaskResult({
77
- params: chainLangDetect(message.content),
78
- })
79
- .then((data) => {
80
- if (data && supportLocales.includes(data)) from = data;
81
- });
82
-
83
- // translate to target language
84
- await chatService.fetchPresetTaskResult({
85
- onMessageHandle: (text) => {
86
- dispatchMessage({
87
- id,
88
- key: 'translate',
89
- type: 'updateMessageExtra',
90
- value: produce({ content: '', from, to: targetLang }, (draft) => {
91
- content += text;
92
- draft.content += content;
93
- }),
94
- });
95
- },
96
- params: chainTranslate(message.content, targetLang),
97
- });
98
-
99
- toggleChatLoading(false);
100
- },
101
-
102
- ttsMessage: (id, init) => {
103
- const { dispatchMessage } = get();
104
- dispatchMessage({
105
- id,
106
- key: 'tts',
107
- type: 'updateMessageExtra',
108
- value: {
109
- init: Boolean(init),
110
- },
111
- });
112
- },
113
- });
@@ -1,9 +0,0 @@
1
- import { OpenAIChatMessage } from '@/types/openai/chat';
2
- import { encodeAsync } from '@/utils/tokenizer';
3
-
4
- export const getMessagesTokenCount = async (messages: OpenAIChatMessage[]) =>
5
- encodeAsync(messages.map((m) => m.content).join(''));
6
-
7
- export const chatHelpers = {
8
- getMessagesTokenCount,
9
- };
@@ -1,13 +0,0 @@
1
- export interface ChatState {
2
- abortController?: AbortController;
3
- activeTopicId?: string;
4
- chatLoadingId?: string;
5
- inputMessage: string;
6
- renameTopicId?: string;
7
- shareLoading?: boolean;
8
- topicLoadingId?: string;
9
- }
10
-
11
- export const initialChatState: ChatState = {
12
- inputMessage: '',
13
- };
@@ -1,319 +0,0 @@
1
- import { ChatMessageMap } from '@/types/chatMessage';
2
-
3
- import { MessageDispatch, messagesReducer } from './message';
4
-
5
- describe('messagesReducer', () => {
6
- let initialState: ChatMessageMap;
7
-
8
- beforeEach(() => {
9
- initialState = {
10
- message1: {
11
- id: 'message1',
12
- content: 'Hello World',
13
- createAt: 1629264000000,
14
- updateAt: 1629264000000,
15
- role: 'user',
16
- },
17
- message2: {
18
- id: 'message2',
19
- content: 'How are you?',
20
- createAt: 1629264000000,
21
- updateAt: 1629264000000,
22
- role: 'system',
23
- },
24
- } as unknown as ChatMessageMap;
25
- });
26
-
27
- describe('addMessage', () => {
28
- it('should add a new message to the state', () => {
29
- const payload: MessageDispatch = {
30
- type: 'addMessage',
31
- message: 'New Message',
32
- role: 'user',
33
- id: 'message3',
34
- };
35
-
36
- const newState = messagesReducer(initialState, payload);
37
-
38
- expect(Object.keys(newState)).toHaveLength(3);
39
- expect(newState).toHaveProperty('message1');
40
- expect(newState).toHaveProperty('message2');
41
- expect(newState).toHaveProperty('message3');
42
- expect(newState.message3).toEqual({
43
- id: 'message3',
44
- content: 'New Message',
45
- meta: {},
46
- createAt: expect.any(Number),
47
- updateAt: expect.any(Number),
48
- role: 'user',
49
- });
50
- });
51
-
52
- it('should use the provided id when adding a new message', () => {
53
- const payload: MessageDispatch = {
54
- type: 'addMessage',
55
- id: 'customId',
56
- message: 'New Message',
57
- role: 'user',
58
- };
59
-
60
- const newState = messagesReducer(initialState, payload);
61
-
62
- expect(Object.keys(newState)).toHaveLength(3);
63
- expect(newState).toHaveProperty('message1');
64
- expect(newState).toHaveProperty('message2');
65
- expect(newState).toHaveProperty('customId');
66
- expect(newState.customId).toEqual({
67
- id: 'customId',
68
- content: 'New Message',
69
- meta: {},
70
- createAt: expect.any(Number),
71
- updateAt: expect.any(Number),
72
- role: 'user',
73
- });
74
- });
75
-
76
- it('should use the provided parentId when adding a new message', () => {
77
- const payload: MessageDispatch = {
78
- type: 'addMessage',
79
- message: 'New Message',
80
- id: 'message3',
81
- role: 'user',
82
- parentId: 'message1',
83
- };
84
-
85
- const newState = messagesReducer(initialState, payload);
86
-
87
- expect(newState.message3).toEqual({
88
- id: 'message3',
89
- content: 'New Message',
90
- meta: {},
91
- createAt: expect.any(Number),
92
- updateAt: expect.any(Number),
93
- role: 'user',
94
- parentId: 'message1',
95
- });
96
- });
97
-
98
- it('should use the provided quotaId when adding a new message', () => {
99
- const payload: MessageDispatch = {
100
- type: 'addMessage',
101
- message: 'New Message',
102
- id: 'message3',
103
- role: 'user',
104
- quotaId: 'message2',
105
- };
106
-
107
- const newState = messagesReducer(initialState, payload);
108
-
109
- expect(newState.message3).toEqual({
110
- id: 'message3',
111
- meta: {},
112
- content: 'New Message',
113
- createAt: expect.any(Number),
114
- updateAt: expect.any(Number),
115
- role: 'user',
116
- quotaId: 'message2',
117
- });
118
- });
119
-
120
- it('should use the provided parentId and quotaId when adding a new message', () => {
121
- const payload: MessageDispatch = {
122
- type: 'addMessage',
123
- message: 'New Message',
124
- id: 'message3',
125
- role: 'user',
126
- parentId: 'message1',
127
- quotaId: 'message2',
128
- };
129
-
130
- const newState = messagesReducer(initialState, payload);
131
-
132
- expect(newState.message3).toEqual({
133
- id: 'message3',
134
- content: 'New Message',
135
- meta: {},
136
- createAt: expect.any(Number),
137
- updateAt: expect.any(Number),
138
- role: 'user',
139
- parentId: 'message1',
140
- quotaId: 'message2',
141
- });
142
- });
143
- });
144
-
145
- describe('deleteMessage', () => {
146
- it('should remove the specified message from the state', () => {
147
- const payload: MessageDispatch = {
148
- type: 'deleteMessage',
149
- id: 'message1',
150
- };
151
-
152
- const newState = messagesReducer(initialState, payload);
153
-
154
- expect(Object.keys(newState)).toHaveLength(1);
155
- expect(newState).not.toHaveProperty('message1');
156
- expect(newState).toHaveProperty('message2');
157
- });
158
-
159
- it('should not modify the state if the specified message does not exist', () => {
160
- const payload: MessageDispatch = {
161
- type: 'deleteMessage',
162
- id: 'nonexistentMessage',
163
- };
164
-
165
- const newState = messagesReducer(initialState, payload);
166
-
167
- expect(newState).toEqual(initialState);
168
- });
169
-
170
- it('should not modify the state if the specified message does not exist', () => {
171
- const payload: MessageDispatch = {
172
- type: 'deleteMessage',
173
- id: 'nonexistentMessage',
174
- };
175
-
176
- const newState = messagesReducer(initialState, payload);
177
-
178
- expect(newState).toEqual(initialState);
179
- });
180
- });
181
-
182
- describe('updateMessage', () => {
183
- it('should update the specified message with the provided value', () => {
184
- const payload: MessageDispatch = {
185
- type: 'updateMessage',
186
- id: 'message1',
187
- key: 'content',
188
- value: 'Updated Message',
189
- };
190
-
191
- const newState = messagesReducer(initialState, payload);
192
-
193
- expect(newState.message1.content).toBe('Updated Message');
194
- expect(newState.message1.updateAt).toBeGreaterThan(initialState.message1.updateAt);
195
- });
196
-
197
- it('should not modify the state if the specified message does not exist', () => {
198
- const payload: MessageDispatch = {
199
- type: 'updateMessage',
200
- id: 'nonexistentMessage',
201
- key: 'content',
202
- value: 'Updated Message',
203
- };
204
-
205
- const newState = messagesReducer(initialState, payload);
206
-
207
- expect(newState).toEqual(initialState);
208
- });
209
-
210
- it('should not modify the state if the specified message does not exist', () => {
211
- const payload: MessageDispatch = {
212
- type: 'updateMessage',
213
- id: 'nonexistentMessage',
214
- key: 'content',
215
- value: 'Updated Message',
216
- };
217
-
218
- const newState = messagesReducer(initialState, payload);
219
-
220
- expect(newState).toEqual(initialState);
221
- });
222
- });
223
-
224
- describe('updateMessageExtra', () => {
225
- it('should update the specified message extra property with the provided value', () => {
226
- const payload: MessageDispatch = {
227
- type: 'updateMessageExtra',
228
- id: 'message1',
229
- key: 'translate',
230
- value: { target: 'en', to: 'zh' },
231
- };
232
-
233
- const newState = messagesReducer(initialState, payload);
234
-
235
- expect(newState.message1.extra!.translate).toEqual({ target: 'en', to: 'zh' });
236
- expect(newState.message1.updateAt).toBeGreaterThan(initialState.message1.updateAt);
237
- });
238
-
239
- it('should not modify the state if the specified message does not exist', () => {
240
- const payload: MessageDispatch = {
241
- type: 'updateMessageExtra',
242
- id: 'nonexistentMessage',
243
- key: 'translate',
244
- value: { target: 'en', to: 'zh' },
245
- };
246
-
247
- const newState = messagesReducer(initialState, payload);
248
-
249
- expect(newState).toEqual(initialState);
250
- });
251
-
252
- it('should not modify the state if the specified message does not exist', () => {
253
- const payload: MessageDispatch = {
254
- type: 'updateMessageExtra',
255
- id: 'nonexistentMessage',
256
- key: 'translate',
257
- value: { target: 'en', to: 'zh' },
258
- };
259
-
260
- const newState = messagesReducer(initialState, payload);
261
-
262
- expect(newState).toEqual(initialState);
263
- });
264
- });
265
-
266
- describe('resetMessages', () => {
267
- it('should reset the state to an empty object', () => {
268
- const payload: MessageDispatch = {
269
- type: 'resetMessages',
270
- };
271
-
272
- const newState = messagesReducer(initialState, payload);
273
-
274
- expect(newState).toEqual({});
275
- });
276
-
277
- it('should delete messages with the specified topicId', () => {
278
- const initialState = {
279
- message1: {
280
- id: 'message1',
281
- content: 'Hello World',
282
- createAt: 1629264000000,
283
- updateAt: 1629264000000,
284
- role: 'user',
285
- topicId: 'topic1',
286
- },
287
- message2: {
288
- id: 'message2',
289
- content: 'How are you?',
290
- createAt: 1629264000000,
291
- updateAt: 1629264000000,
292
- role: 'system',
293
- },
294
- } as unknown as ChatMessageMap;
295
-
296
- const payload: MessageDispatch = {
297
- type: 'resetMessages',
298
- topicId: 'topic1',
299
- };
300
-
301
- const newState = messagesReducer(initialState, payload);
302
-
303
- expect(Object.keys(newState)).toHaveLength(1);
304
- expect(newState).not.toHaveProperty('message1');
305
- expect(newState).toHaveProperty('message2');
306
- });
307
- });
308
-
309
- describe('unimplemented type', () => {
310
- it('should throw an error when an unimplemented type is provided', () => {
311
- // @ts-ignore
312
- const payload: MessageDispatch = { type: 'unimplementedType' };
313
-
314
- expect(() => messagesReducer(initialState, payload)).toThrowError(
315
- '暂未实现的 type,请检查 reducer',
316
- );
317
- });
318
- });
319
- });
@@ -1,156 +0,0 @@
1
- import isEqual from 'fast-deep-equal';
2
- import { produce } from 'immer';
3
-
4
- import { ChatMessage, ChatMessageMap } from '@/types/chatMessage';
5
- import { LLMRoleType } from '@/types/llm';
6
- import { MetaData } from '@/types/meta';
7
- import { merge } from '@/utils/merge';
8
- import { nanoid } from '@/utils/uuid';
9
-
10
- interface AddMessage {
11
- id?: string;
12
- message: string;
13
- meta?: MetaData;
14
- parentId?: string;
15
- quotaId?: string;
16
- role: LLMRoleType;
17
- type: 'addMessage';
18
- }
19
-
20
- interface DeleteMessage {
21
- id: string;
22
- type: 'deleteMessage';
23
- }
24
-
25
- interface ResetMessages {
26
- topicId?: string;
27
- type: 'resetMessages';
28
- }
29
-
30
- interface UpdateMessage {
31
- id: string;
32
- key: keyof ChatMessage;
33
- type: 'updateMessage';
34
- value: ChatMessage[keyof ChatMessage];
35
- }
36
- interface UpdateMessageExtra {
37
- id: string;
38
- key: string;
39
- type: 'updateMessageExtra';
40
- value: any;
41
- }
42
-
43
- interface UpdatePluginState {
44
- id: string;
45
- key: string;
46
- type: 'updatePluginState';
47
- value: any;
48
- }
49
-
50
- export type MessageDispatch =
51
- | AddMessage
52
- | DeleteMessage
53
- | ResetMessages
54
- | UpdateMessage
55
- | UpdateMessageExtra
56
- | UpdatePluginState;
57
-
58
- export const messagesReducer = (
59
- state: ChatMessageMap,
60
- payload: MessageDispatch,
61
- ): ChatMessageMap => {
62
- switch (payload.type) {
63
- case 'addMessage': {
64
- return produce(state, (draftState) => {
65
- const mid = payload.id || nanoid();
66
-
67
- draftState[mid] = {
68
- content: payload.message,
69
- createAt: Date.now(),
70
- id: mid,
71
- meta: payload.meta || {},
72
- parentId: payload.parentId,
73
- quotaId: payload.quotaId,
74
- role: payload.role,
75
- updateAt: Date.now(),
76
- };
77
- });
78
- }
79
-
80
- case 'deleteMessage': {
81
- return produce(state, (draftState) => {
82
- delete draftState[payload.id];
83
- });
84
- }
85
-
86
- case 'updateMessage': {
87
- return produce(state, (draftState) => {
88
- const { id, key, value } = payload;
89
- const message = draftState[id];
90
- if (!message) return;
91
-
92
- // @ts-ignore
93
- message[key] = value;
94
- message.updateAt = Date.now();
95
- });
96
- }
97
-
98
- case 'updateMessageExtra': {
99
- return produce(state, (draftState) => {
100
- const { id, key, value } = payload;
101
- const message = draftState[id];
102
- if (!message) return;
103
-
104
- if (!message.extra) {
105
- message.extra = { [key]: value } as any;
106
- } else {
107
- message.extra[key] = value;
108
- }
109
-
110
- message.updateAt = Date.now();
111
- });
112
- }
113
-
114
- case 'updatePluginState': {
115
- return produce(state, (draftState) => {
116
- const { id, key, value } = payload;
117
- const message = draftState[id];
118
- if (!message) return;
119
-
120
- let newState;
121
- if (!message.pluginState) {
122
- newState = { [key]: value } as any;
123
- } else {
124
- newState = merge(message.pluginState, { [key]: value });
125
- }
126
-
127
- if (isEqual(message.pluginState, newState)) return;
128
-
129
- message.pluginState = newState;
130
- message.updateAt = Date.now();
131
- });
132
- }
133
-
134
- case 'resetMessages': {
135
- return produce(state, (draftState) => {
136
- const { topicId } = payload;
137
-
138
- const messages = Object.values(draftState).filter((message) => {
139
- // 如果没有 topicId,说明是清空默认对话里的消息
140
- if (!topicId) return !message.topicId;
141
-
142
- return message.topicId === topicId;
143
- });
144
-
145
- // 删除上述找到的消息
146
- for (const message of messages) {
147
- delete draftState[message.id];
148
- }
149
- });
150
- }
151
-
152
- default: {
153
- throw new Error('暂未实现的 type,请检查 reducer');
154
- }
155
- }
156
- };