@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
@@ -1,11 +1,25 @@
1
+ import { VersionController } from '@/migrations/VersionController';
1
2
  import { ConfigStateAll } from '@/types/exportConfig';
2
- import { VersionController } from '@/utils/VersionController';
3
+
4
+ import { MigrationV0ToV1 } from './FromV0ToV1';
5
+ import { MigrationV1ToV2 } from './FromV1ToV2';
3
6
 
4
7
  // 当前最新的版本号
5
- export const CURRENT_CONFIG_VERSION = 1;
8
+ export const CURRENT_CONFIG_VERSION = 2;
6
9
 
7
10
  // 历史记录版本升级模块
8
- export const ConfigMigrations = [];
11
+ const ConfigMigrations = [
12
+ /**
13
+ * 2023.11.27
14
+ * 从单 key 数据库转换为基于 dexie 的关系型结构
15
+ */
16
+ MigrationV1ToV2,
17
+ /**
18
+ * 2023.07.11
19
+ * just the first version, Nothing to do
20
+ */
21
+ MigrationV0ToV1,
22
+ ];
9
23
 
10
24
  export const Migration = new VersionController<ConfigStateAll>(
11
25
  ConfigMigrations,
@@ -1,7 +1,7 @@
1
1
  import { Mock, beforeEach, describe, expect, it, vi } from 'vitest';
2
2
 
3
3
  import { FileModel } from '@/database/models/file';
4
- import { LocalFile } from '@/types/database/files';
4
+ import { DB_File } from '@/database/schemas/files';
5
5
 
6
6
  import { fileService } from '../file';
7
7
 
@@ -25,7 +25,7 @@ describe('FileService', () => {
25
25
  });
26
26
 
27
27
  it('uploadFile should save the file to the database', async () => {
28
- const localFile: LocalFile = {
28
+ const localFile: DB_File = {
29
29
  name: 'test',
30
30
  data: new ArrayBuffer(1),
31
31
  fileType: 'image/png',
@@ -53,7 +53,7 @@ describe('FileService', () => {
53
53
 
54
54
  it('getFile should retrieve and convert file info to FilePreview', async () => {
55
55
  const fileId = '1';
56
- const fileData: LocalFile = {
56
+ const fileData: DB_File = {
57
57
  name: 'test',
58
58
  data: new ArrayBuffer(1),
59
59
  fileType: 'image/png',
@@ -3,9 +3,12 @@ import { merge } from 'lodash-es';
3
3
 
4
4
  import { VISION_MODEL_WHITE_LIST } from '@/const/llm';
5
5
  import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
6
+ import { filesSelectors, useFileStore } from '@/store/files';
6
7
  import { usePluginStore } from '@/store/plugin';
7
8
  import { pluginSelectors } from '@/store/plugin/selectors';
8
- import type { OpenAIChatStreamPayload } from '@/types/openai/chat';
9
+ import { ChatMessage } from '@/types/chatMessage';
10
+ import type { OpenAIChatMessage, OpenAIChatStreamPayload } from '@/types/openai/chat';
11
+ import { UserMessageContentPart } from '@/types/openai/chat';
9
12
  import { fetchAIFactory, getMessageError } from '@/utils/fetch';
10
13
 
11
14
  import { createHeaderWithOpenAI } from './_header';
@@ -15,9 +18,13 @@ interface FetchOptions {
15
18
  signal?: AbortSignal | undefined;
16
19
  }
17
20
 
21
+ interface GetChatCompletionPayload extends Partial<Omit<OpenAIChatStreamPayload, 'messages'>> {
22
+ messages: ChatMessage[];
23
+ }
24
+
18
25
  class ChatService {
19
- getChatCompletion = (
20
- { plugins: enabledPlugins, ...params }: Partial<OpenAIChatStreamPayload>,
26
+ createAssistantMessage = async (
27
+ { plugins: enabledPlugins, messages, ...params }: GetChatCompletionPayload,
21
28
  options?: FetchOptions,
22
29
  ) => {
23
30
  const payload = merge(
@@ -28,7 +35,11 @@ class ChatService {
28
35
  },
29
36
  params,
30
37
  );
31
- // ============ preprocess tools ============ //
38
+ // ============ 1. preprocess messages ============ //
39
+
40
+ const oaiMessages = this.processMessages(messages);
41
+
42
+ // ============ 2. preprocess tools ============ //
32
43
 
33
44
  const filterTools = pluginSelectors.enabledSchema(enabledPlugins)(usePluginStore.getState());
34
45
 
@@ -41,8 +52,21 @@ class ChatService {
41
52
 
42
53
  const functions = shouldUseTools ? filterTools : undefined;
43
54
 
55
+ return this.getChatCompletion({ ...params, functions, messages: oaiMessages }, options);
56
+ };
57
+
58
+ getChatCompletion = (params: Partial<OpenAIChatStreamPayload>, options?: FetchOptions) => {
59
+ const payload = merge(
60
+ {
61
+ model: DEFAULT_AGENT_CONFIG.model,
62
+ stream: true,
63
+ ...DEFAULT_AGENT_CONFIG.params,
64
+ },
65
+ params,
66
+ );
67
+
44
68
  return fetch(OPENAI_URLS.chat, {
45
- body: JSON.stringify({ ...payload, functions }),
69
+ body: JSON.stringify(payload),
46
70
  headers: createHeaderWithOpenAI({ 'Content-Type': 'application/json' }),
47
71
  method: 'POST',
48
72
  signal: options?.signal,
@@ -79,6 +103,43 @@ class ChatService {
79
103
  };
80
104
 
81
105
  fetchPresetTaskResult = fetchAIFactory(this.getChatCompletion);
106
+
107
+ private processMessages = (messages: ChatMessage[]): OpenAIChatMessage[] => {
108
+ // handle content type for vision model
109
+ // for the models with visual ability, add image url to content
110
+ // refs: https://platform.openai.com/docs/guides/vision/quick-start
111
+ const getContent = (m: ChatMessage) => {
112
+ if (!m.files) return m.content;
113
+
114
+ const imageList = filesSelectors.getImageUrlOrBase64ByList(m.files)(useFileStore.getState());
115
+
116
+ if (imageList.length === 0) return m.content;
117
+
118
+ return [
119
+ { text: m.content, type: 'text' },
120
+ ...imageList.map(
121
+ (i) => ({ image_url: { detail: 'auto', url: i.url }, type: 'image_url' }) as const,
122
+ ),
123
+ ] as UserMessageContentPart[];
124
+ };
125
+
126
+ return messages.map((m): OpenAIChatMessage => {
127
+ switch (m.role) {
128
+ case 'user': {
129
+ return { content: getContent(m), role: m.role };
130
+ }
131
+
132
+ case 'function': {
133
+ const name = m.plugin?.identifier as string;
134
+ return { content: m.content, name, role: m.role };
135
+ }
136
+
137
+ default: {
138
+ return { content: m.content, role: m.role };
139
+ }
140
+ }
141
+ });
142
+ };
82
143
  }
83
144
 
84
145
  export const chatService = new ChatService();
@@ -0,0 +1,184 @@
1
+ import { messageService } from '@/services/message';
2
+ import { sessionService } from '@/services/session';
3
+ import { topicService } from '@/services/topic';
4
+ import { settingsSelectors, useGlobalStore } from '@/store/global';
5
+ import { useSessionStore } from '@/store/session';
6
+ import { sessionExportSelectors, sessionSelectors } from '@/store/session/selectors';
7
+ import { ChatMessage } from '@/types/chatMessage';
8
+ import { ConfigFile } from '@/types/exportConfig';
9
+ import { LobeSessions } from '@/types/session';
10
+ import { GlobalSettings } from '@/types/settings';
11
+ import { ChatTopic } from '@/types/topic';
12
+ import { createConfigFile, exportConfigFile } from '@/utils/config';
13
+
14
+ export interface ImportResult {
15
+ added: number;
16
+ errors: number;
17
+ skips: number;
18
+ }
19
+ export interface ImportResults {
20
+ messages?: ImportResult;
21
+ sessions: ImportResult;
22
+ topics?: ImportResult;
23
+ }
24
+
25
+ class ConfigService {
26
+ /**
27
+ * import sessions from files
28
+ * @param sessions
29
+ */
30
+ importSessions = async (sessions: LobeSessions) => {
31
+ return await sessionService.batchCreateSessions(sessions);
32
+ };
33
+ importMessages = async (messages: ChatMessage[]) => {
34
+ return messageService.batchCreate(messages);
35
+ };
36
+ importSettings = async (settings: GlobalSettings) => {
37
+ useGlobalStore.getState().importAppSettings(settings);
38
+ };
39
+ importTopics = async (topics: ChatTopic[]) => {
40
+ return topicService.batchCreateTopics(topics);
41
+ };
42
+
43
+ importConfigState = async (config: ConfigFile): Promise<ImportResults | undefined> => {
44
+ switch (config.exportType) {
45
+ case 'settings': {
46
+ await this.importSettings(config.state.settings);
47
+
48
+ break;
49
+ }
50
+
51
+ case 'agents': {
52
+ const data = await this.importSessions(config.state.sessions);
53
+ return {
54
+ sessions: this.mapImportResult(data),
55
+ };
56
+ }
57
+
58
+ case 'all': {
59
+ await this.importSettings(config.state.settings);
60
+
61
+ const [sessions, messages, topics] = await Promise.all([
62
+ this.importSessions(config.state.sessions),
63
+ this.importMessages(config.state.messages),
64
+ this.importTopics(config.state.topics),
65
+ ]);
66
+
67
+ return {
68
+ messages: this.mapImportResult(messages),
69
+ sessions: this.mapImportResult(sessions),
70
+ topics: this.mapImportResult(topics),
71
+ };
72
+ }
73
+
74
+ case 'sessions': {
75
+ const [sessions, messages, topics] = await Promise.all([
76
+ this.importSessions(config.state.sessions),
77
+ this.importMessages(config.state.messages),
78
+ this.importTopics(config.state.topics),
79
+ ]);
80
+
81
+ return {
82
+ messages: this.mapImportResult(messages),
83
+ sessions: this.mapImportResult(sessions),
84
+ topics: this.mapImportResult(topics),
85
+ };
86
+ }
87
+ }
88
+ };
89
+
90
+ /**
91
+ * export all agents
92
+ */
93
+ exportAgents = async () => {
94
+ const agents = await sessionService.getAllAgents();
95
+
96
+ const config = createConfigFile('agents', { sessions: agents });
97
+
98
+ exportConfigFile(config, 'agents');
99
+ };
100
+
101
+ /**
102
+ * export all sessions
103
+ */
104
+ exportSessions = async () => {
105
+ const sessions = await sessionService.getSessions();
106
+ const messages = await messageService.getAllMessages();
107
+ const topics = await topicService.getAllTopics();
108
+
109
+ const config = createConfigFile('sessions', { messages, sessions, topics });
110
+
111
+ exportConfigFile(config, 'sessions');
112
+ };
113
+
114
+ /**
115
+ * export a session
116
+ */
117
+ exportSingleSession = async (id: string) => {
118
+ const session = this.getSession(id);
119
+ if (!session) return;
120
+
121
+ const messages = await messageService.getMessages(id);
122
+ const topics = await topicService.getTopics({ sessionId: id });
123
+
124
+ const config = createConfigFile('singleSession', { messages, sessions: [session], topics });
125
+
126
+ exportConfigFile(config, `${session.meta?.title}-session`);
127
+ };
128
+
129
+ exportSingleAgent = async (id: string) => {
130
+ const agent = this.getAgent(id);
131
+ if (!agent) return;
132
+
133
+ const config = createConfigFile('agents', { sessions: [agent] });
134
+
135
+ exportConfigFile(config, agent.meta?.title || 'agent');
136
+ };
137
+
138
+ /**
139
+ * export settings
140
+ */
141
+ exportSettings = async () => {
142
+ const settings = this.getSettings();
143
+
144
+ const config = createConfigFile('settings', { settings });
145
+
146
+ exportConfigFile(config, 'settings');
147
+ };
148
+
149
+ /**
150
+ * export all data
151
+ */
152
+ exportAll = async () => {
153
+ const sessions = await sessionService.getSessions();
154
+ const messages = await messageService.getAllMessages();
155
+ const topics = await topicService.getAllTopics();
156
+ const settings = this.getSettings();
157
+
158
+ const config = createConfigFile('all', { messages, sessions, settings, topics });
159
+
160
+ exportConfigFile(config, 'config');
161
+ };
162
+
163
+ private getSettings = () => settingsSelectors.exportSettings(useGlobalStore.getState());
164
+
165
+ private getSession = (id: string) =>
166
+ sessionSelectors.getSessionById(id)(useSessionStore.getState());
167
+
168
+ private getAgent = (id: string) =>
169
+ sessionExportSelectors.getExportAgent(id)(useSessionStore.getState());
170
+
171
+ private mapImportResult = (input: {
172
+ added: number;
173
+ errors?: Error[];
174
+ skips: string[];
175
+ }): ImportResult => {
176
+ return {
177
+ added: input.added,
178
+ errors: input.errors?.length || 0,
179
+ skips: input.skips.length,
180
+ };
181
+ };
182
+ }
183
+
184
+ export const configService = new ConfigService();
@@ -0,0 +1,13 @@
1
+ import { DEBUG_MODEL } from '@/database/models/__DEBUG';
2
+
3
+ class DebugService {
4
+ async insertLargeDataToDB() {
5
+ return DEBUG_MODEL.createRandomData({
6
+ messageCount: 400_000,
7
+ sessionCount: 40,
8
+ topicCount: 200,
9
+ });
10
+ }
11
+ }
12
+
13
+ export const debugService = new DebugService();
@@ -1,9 +1,9 @@
1
1
  import { FileModel } from '@/database/models/file';
2
- import { LocalFile } from '@/types/database/files';
2
+ import { DB_File } from '@/database/schemas/files';
3
3
  import { FilePreview } from '@/types/files';
4
4
 
5
5
  class FileService {
6
- async uploadFile(file: LocalFile) {
6
+ async uploadFile(file: DB_File) {
7
7
  // save to local storage
8
8
  // we may want to save to a remote server later
9
9
  return FileModel.create(file);
@@ -13,6 +13,10 @@ class FileService {
13
13
  return FileModel.delete(id);
14
14
  }
15
15
 
16
+ async removeAllFiles() {
17
+ return FileModel.clear();
18
+ }
19
+
16
20
  async getFile(id: string): Promise<FilePreview> {
17
21
  const item = await FileModel.findById(id);
18
22
  if (!item) {
@@ -0,0 +1,25 @@
1
+ import qs from 'query-string';
2
+ import urlJoin from 'url-join';
3
+
4
+ import { GITHUB } from '@/const/url';
5
+
6
+ class GitHubService {
7
+ submitDBV1UpgradeError = (version: number, error?: { message: string }) => {
8
+ const body = ['```json', JSON.stringify(error, null, 2), '```'].join('\n');
9
+
10
+ const message = error?.message || '';
11
+
12
+ const url = qs.stringifyUrl({
13
+ query: {
14
+ body,
15
+ labels: '❌ Database Migration Error',
16
+ title: `[Migration Error V${version}] ${message}`,
17
+ },
18
+ url: urlJoin(GITHUB, '/issues/new'),
19
+ });
20
+
21
+ window.open(url, '_blank');
22
+ };
23
+ }
24
+
25
+ export const githubService = new GitHubService();
@@ -0,0 +1,79 @@
1
+ import { CreateMessageParams, MessageModel } from '@/database/models/message';
2
+ import {
3
+ ChatMessage,
4
+ ChatMessageError,
5
+ ChatPluginPayload,
6
+ ChatTTS,
7
+ ChatTranslate,
8
+ } from '@/types/chatMessage';
9
+ import { LLMRoleType } from '@/types/llm';
10
+
11
+ export class MessageService {
12
+ async create(data: CreateMessageParams) {
13
+ const { id } = await MessageModel.create(data);
14
+
15
+ return id;
16
+ }
17
+
18
+ async batchCreate(messages: ChatMessage[]) {
19
+ return MessageModel.batchCreate(messages);
20
+ }
21
+
22
+ async hasMessages() {
23
+ const isEmpty = await MessageModel.isEmpty();
24
+ return !isEmpty;
25
+ }
26
+
27
+ async getMessages(sessionId: string, topicId?: string): Promise<ChatMessage[]> {
28
+ return MessageModel.query({ sessionId, topicId });
29
+ }
30
+
31
+ async removeMessage(id: string) {
32
+ return MessageModel.delete(id);
33
+ }
34
+
35
+ async updateMessageContent(id: string, content: string) {
36
+ return MessageModel.update(id, { content });
37
+ }
38
+
39
+ async updateMessageError(id: string, error: ChatMessageError) {
40
+ return MessageModel.update(id, { error });
41
+ }
42
+
43
+ async updateMessageTranslate(id: string, data: Partial<ChatTranslate> | null) {
44
+ return MessageModel.update(id, { translate: data as ChatTranslate });
45
+ }
46
+
47
+ async updateMessageTTS(id: string, data: Partial<ChatTTS> | null) {
48
+ return MessageModel.update(id, { tts: data as ChatTTS });
49
+ }
50
+
51
+ async removeMessages(assistantId: string, topicId?: string) {
52
+ return MessageModel.batchDelete(assistantId, topicId);
53
+ }
54
+
55
+ async clearAllMessage() {
56
+ return MessageModel.clearTable();
57
+ }
58
+
59
+ async bindMessagesToTopic(topicId: string, messageIds: string[]) {
60
+ return MessageModel.batchUpdate(messageIds, { topicId });
61
+ }
62
+
63
+ async updateMessageRole(id: string, role: LLMRoleType) {
64
+ return MessageModel.update(id, { role });
65
+ }
66
+
67
+ async updateMessagePlugin(id: string, plugin: ChatPluginPayload) {
68
+ return MessageModel.update(id, { plugin });
69
+ }
70
+ async updateMessagePluginState(id: string, key: string, value: any) {
71
+ return MessageModel.update(id, { pluginState: { [key]: value } });
72
+ }
73
+
74
+ async getAllMessages() {
75
+ return MessageModel.queryAll();
76
+ }
77
+ }
78
+
79
+ export const messageService = new MessageService();
@@ -0,0 +1,63 @@
1
+ import { DeepPartial } from 'utility-types';
2
+
3
+ import { SessionModel } from '@/database/models/session';
4
+ import { LobeAgentConfig } from '@/types/agent';
5
+ import { MetaData } from '@/types/meta';
6
+ import { LobeAgentSession, LobeSessionType, LobeSessions, SessionGroupKey } from '@/types/session';
7
+
8
+ class SessionService {
9
+ async createNewSession(
10
+ type: LobeSessionType,
11
+ defaultValue: Partial<LobeAgentSession>,
12
+ ): Promise<string> {
13
+ const item = await SessionModel.create(type, defaultValue);
14
+ if (!item) {
15
+ throw new Error('session create Error');
16
+ }
17
+ return item.id;
18
+ }
19
+
20
+ async batchCreateSessions(importSessions: LobeSessions) {
21
+ return SessionModel.batchCreate(importSessions);
22
+ }
23
+
24
+ async getSessions(): Promise<LobeSessions> {
25
+ return SessionModel.query();
26
+ }
27
+
28
+ async getAllAgents(): Promise<LobeSessions> {
29
+ // TODO: add a filter to get only agents
30
+ return await SessionModel.query();
31
+ }
32
+
33
+ async removeSession(id: string) {
34
+ return SessionModel.delete(id);
35
+ }
36
+
37
+ async removeAllSessions() {
38
+ return SessionModel.clearTable();
39
+ }
40
+
41
+ async updateSessionGroup(id: string, group: SessionGroupKey) {
42
+ return SessionModel.update(id, { group });
43
+ }
44
+
45
+ async updateSessionMeta(activeId: string, meta: Partial<MetaData>) {
46
+ return SessionModel.update(activeId, { meta });
47
+ }
48
+
49
+ async updateSessionConfig(activeId: string, config: DeepPartial<LobeAgentConfig>) {
50
+ return SessionModel.updateConfig(activeId, config);
51
+ }
52
+
53
+ async hasSessions() {
54
+ const isEmpty = await SessionModel.isEmpty();
55
+ return !isEmpty;
56
+ }
57
+
58
+ async searchSessions(keyword: string) {
59
+ return SessionModel.queryByKeyword(keyword);
60
+ }
61
+ }
62
+
63
+ export const sessionService = new SessionService();
@@ -0,0 +1,56 @@
1
+ import { CreateTopicParams, QueryTopicParams, TopicModel } from '@/database/models/topic';
2
+ import { ChatTopic } from '@/types/topic';
3
+
4
+ class TopicService {
5
+ async createTopic(params: CreateTopicParams): Promise<string> {
6
+ const item = await TopicModel.create(params);
7
+
8
+ if (!item) {
9
+ throw new Error('topic create Error');
10
+ }
11
+
12
+ return item.id;
13
+ }
14
+
15
+ async getTopics(params: QueryTopicParams): Promise<ChatTopic[]> {
16
+ return TopicModel.query(params);
17
+ }
18
+
19
+ async removeTopic(id: string) {
20
+ return TopicModel.delete(id);
21
+ }
22
+
23
+ async removeTopics(sessionId: string) {
24
+ return TopicModel.batchDeleteBySessionId(sessionId);
25
+ }
26
+
27
+ async batchRemoveTopics(topics: string[]) {
28
+ return TopicModel.batchDelete(topics);
29
+ }
30
+
31
+ async removeAllTopic() {
32
+ return TopicModel.clearTable();
33
+ }
34
+
35
+ async updateFavorite(id: string, newState?: boolean) {
36
+ return TopicModel.toggleFavorite(id, newState);
37
+ }
38
+
39
+ async batchCreateTopics(importTopics: ChatTopic[]) {
40
+ return TopicModel.batchCreate(importTopics as any);
41
+ }
42
+
43
+ updateTitle(topicId: string, text: string) {
44
+ return TopicModel.update(topicId, { title: text });
45
+ }
46
+
47
+ async getAllTopics() {
48
+ return TopicModel.queryAll();
49
+ }
50
+
51
+ async searchTopics(keyword: string) {
52
+ return TopicModel.queryByKeyword(keyword);
53
+ }
54
+ }
55
+
56
+ export const topicService = new TopicService();