@lobehub/chat 0.106.0 → 0.107.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/locales/en_US/common.json +19 -0
  3. package/locales/en_US/migration.json +45 -0
  4. package/locales/en_US/setting.json +7 -7
  5. package/locales/ja_JP/common.json +19 -0
  6. package/locales/ja_JP/migration.json +45 -0
  7. package/locales/ja_JP/setting.json +2 -2
  8. package/locales/ko_KR/common.json +19 -0
  9. package/locales/ko_KR/migration.json +45 -0
  10. package/locales/ko_KR/setting.json +2 -2
  11. package/locales/ru_RU/common.json +19 -0
  12. package/locales/ru_RU/migration.json +45 -0
  13. package/locales/ru_RU/setting.json +2 -2
  14. package/locales/zh_CN/common.json +19 -0
  15. package/locales/zh_CN/migration.json +45 -0
  16. package/locales/zh_CN/setting.json +1 -1
  17. package/locales/zh_TW/common.json +19 -0
  18. package/locales/zh_TW/migration.json +45 -0
  19. package/locales/zh_TW/setting.json +2 -2
  20. package/package.json +2 -1
  21. package/src/app/api/openai/chat/createChatCompletion.test.ts +4 -4
  22. package/src/app/api/openai/chat/createChatCompletion.ts +4 -10
  23. package/src/app/api/openai/createBizOpenAI/index.ts +1 -1
  24. package/src/app/api/openai/errorResponse.test.ts +1 -1
  25. package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +2 -1
  26. package/src/app/chat/(mobile)/features/ChatInput/SendButton.tsx +2 -2
  27. package/src/app/chat/features/ChatHeader/ShareButton/Inner.tsx +2 -5
  28. package/src/app/chat/features/ChatHeader/ShareButton/index.tsx +2 -2
  29. package/src/app/chat/features/ChatInput/ActionBar/Clear.tsx +2 -2
  30. package/src/app/chat/features/ChatInput/ActionBar/Token/TokenTag.tsx +8 -2
  31. package/src/app/chat/features/ChatInput/InputAreaInner/index.tsx +2 -2
  32. package/src/app/chat/features/ChatInput/STT/index.tsx +2 -2
  33. package/src/app/chat/features/ChatInput/Topic/index.tsx +2 -2
  34. package/src/app/chat/features/ChatInput/useSend.ts +3 -3
  35. package/src/app/chat/features/Conversation/ChatList/Actions/index.ts +2 -2
  36. package/src/app/chat/features/Conversation/ChatList/Error/ApiKeyForm.tsx +2 -2
  37. package/src/app/chat/features/Conversation/ChatList/Error/InvalidAccess.tsx +2 -2
  38. package/src/app/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +2 -2
  39. package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.test.tsx +4 -4
  40. package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.tsx +30 -31
  41. package/src/app/chat/features/Conversation/ChatList/Extras/TTS.tsx +2 -2
  42. package/src/app/chat/features/Conversation/ChatList/Extras/Translate.tsx +2 -2
  43. package/src/app/chat/features/Conversation/ChatList/Extras/User.tsx +8 -5
  44. package/src/app/chat/features/Conversation/ChatList/Extras/index.ts +3 -3
  45. package/src/app/chat/features/Conversation/ChatList/Extras/type.ts +5 -0
  46. package/src/app/chat/features/Conversation/ChatList/Messages/Assistant.tsx +10 -6
  47. package/src/app/chat/features/Conversation/ChatList/Messages/Default.tsx +7 -3
  48. package/src/app/chat/features/Conversation/ChatList/Messages/Function.tsx +7 -6
  49. package/src/app/chat/features/Conversation/ChatList/Messages/index.ts +7 -7
  50. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/Iframe.tsx +6 -6
  51. package/src/app/chat/features/Conversation/ChatList/index.tsx +16 -24
  52. package/src/app/chat/features/Conversation/ScrollAnchor/index.tsx +2 -2
  53. package/src/app/chat/features/Conversation/index.tsx +3 -3
  54. package/src/app/chat/features/Conversation/useInitConversation.ts +43 -0
  55. package/src/app/chat/features/Migration/ExportConfigButton.tsx +33 -0
  56. package/src/app/chat/features/Migration/Failed.tsx +115 -0
  57. package/src/app/chat/features/Migration/Modal.tsx +80 -0
  58. package/src/app/chat/features/Migration/Start.tsx +106 -0
  59. package/src/app/chat/features/Migration/UpgradeButton.tsx +74 -0
  60. package/src/app/chat/features/Migration/const.ts +15 -0
  61. package/src/app/chat/features/Migration/index.tsx +44 -0
  62. package/src/app/chat/features/SessionListContent/DefaultMode.tsx +54 -0
  63. package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +5 -4
  64. package/src/app/chat/features/SessionListContent/List/Item/index.tsx +22 -28
  65. package/src/app/chat/features/SessionListContent/SearchMode.tsx +15 -0
  66. package/src/app/chat/features/SessionListContent/index.tsx +4 -44
  67. package/src/app/chat/features/SessionSearchBar/index.tsx +11 -3
  68. package/src/app/chat/features/TopicListContent/Header.tsx +4 -4
  69. package/src/app/chat/features/TopicListContent/Topic/TopicContent.tsx +11 -9
  70. package/src/app/chat/features/TopicListContent/Topic/TopicItem.tsx +15 -13
  71. package/src/app/chat/features/TopicListContent/Topic/index.tsx +9 -16
  72. package/src/app/chat/features/TopicListContent/TopicSearchBar/index.tsx +13 -4
  73. package/src/app/chat/features/TopicListContent/index.tsx +1 -2
  74. package/src/app/chat/page.tsx +6 -1
  75. package/src/app/chat/settings/features/HeaderContent.tsx +3 -3
  76. package/src/app/home/Redirect.tsx +13 -7
  77. package/src/app/settings/(mobile)/features/ExtraList.tsx +12 -4
  78. package/src/app/settings/common/Common.tsx +16 -4
  79. package/src/app/welcome/features/Banner/index.tsx +9 -10
  80. package/src/chains/summaryTitle.ts +5 -2
  81. package/src/components/DataStyleModal/index.tsx +64 -0
  82. package/src/config/client.ts +5 -0
  83. package/src/const/session.ts +21 -0
  84. package/src/const/settings.ts +1 -1
  85. package/src/database/core/__tests__/db.test.ts +8 -5
  86. package/src/database/core/__tests__/model.test.ts +3 -3
  87. package/src/database/core/db.ts +38 -10
  88. package/src/database/core/index.ts +1 -0
  89. package/src/database/core/model.ts +121 -10
  90. package/src/database/core/schemas.ts +22 -0
  91. package/src/database/core/types/db.ts +13 -0
  92. package/src/database/models/__DEBUG.ts +119 -0
  93. package/src/database/models/{file.test.ts → __tests__/file.test.ts} +19 -6
  94. package/src/database/models/__tests__/message.test.ts +314 -0
  95. package/src/database/models/__tests__/session.test.ts +117 -0
  96. package/src/database/models/__tests__/topic.test.ts +250 -0
  97. package/src/database/models/file.ts +9 -5
  98. package/src/database/models/message.ts +186 -0
  99. package/src/database/models/session.ts +160 -0
  100. package/src/database/models/topic.ts +196 -0
  101. package/src/{types/database → database/schemas}/files.ts +2 -2
  102. package/src/database/schemas/message.ts +37 -0
  103. package/src/database/schemas/session.ts +56 -0
  104. package/src/database/schemas/topic.ts +12 -0
  105. package/src/features/AgentSetting/AgentTTS/SelectWithTTSPreview.tsx +1 -1
  106. package/src/features/AgentSetting/store/action.ts +1 -1
  107. package/src/features/AgentSetting/store/initialState.ts +1 -1
  108. package/src/features/AgentSetting/store/reducers/config.ts +1 -1
  109. package/src/features/DataImporter/index.tsx +240 -0
  110. package/src/features/DebugUI/index.tsx +34 -0
  111. package/src/features/SideBar/BottomActions.tsx +8 -14
  112. package/src/hooks/useImportConfig.ts +13 -30
  113. package/src/hooks/useTTS.ts +1 -1
  114. package/src/layout/GlobalLayout/index.tsx +16 -1
  115. package/src/locales/default/common.ts +19 -0
  116. package/src/locales/default/migration.ts +47 -0
  117. package/src/locales/default/setting.ts +1 -1
  118. package/src/locales/resources/en_US.ts +2 -0
  119. package/src/locales/resources/ja_JP.ts +2 -0
  120. package/src/locales/resources/ko_KR.ts +2 -0
  121. package/src/locales/resources/ru_RU.ts +2 -0
  122. package/src/locales/resources/zh_CN.ts +2 -0
  123. package/src/locales/resources/zh_TW.ts +2 -0
  124. package/src/migrations/FromV0ToV1.ts +2 -4
  125. package/src/migrations/FromV1ToV2/fixtures/input-v1-session.json +191 -0
  126. package/src/migrations/FromV1ToV2/fixtures/output-v2.json +202 -0
  127. package/src/migrations/FromV1ToV2/index.ts +82 -0
  128. package/src/migrations/FromV1ToV2/migrations.test.ts +224 -0
  129. package/src/migrations/FromV1ToV2/types/v1.ts +78 -0
  130. package/src/migrations/FromV1ToV2/types/v2.ts +52 -0
  131. package/src/{utils → migrations}/VersionController.test.ts +1 -3
  132. package/src/{utils → migrations}/VersionController.ts +5 -2
  133. package/src/migrations/index.ts +17 -3
  134. package/src/services/__tests__/file.test.ts +3 -3
  135. package/src/services/chat.ts +66 -5
  136. package/src/services/config.ts +184 -0
  137. package/src/services/debug.ts +13 -0
  138. package/src/services/file.ts +6 -2
  139. package/src/services/github.ts +25 -0
  140. package/src/services/message.ts +79 -0
  141. package/src/services/session.ts +63 -0
  142. package/src/services/topic.ts +56 -0
  143. package/src/store/chat/actions/enhance.ts +95 -0
  144. package/src/store/{session/slices/chat → chat}/actions/message.ts +225 -247
  145. package/src/store/{session/slices/chat → chat}/actions/plugin.ts +29 -34
  146. package/src/store/{session/slices/chat → chat}/actions/share.ts +9 -13
  147. package/src/store/chat/actions/topic.ts +197 -0
  148. package/src/store/chat/helpers.ts +23 -0
  149. package/src/store/chat/index.ts +3 -0
  150. package/src/store/chat/initialState.ts +48 -0
  151. package/src/store/chat/reducers/message.test.ts +83 -0
  152. package/src/store/chat/reducers/message.ts +83 -0
  153. package/src/store/chat/selectors/__tests__/topic.test.ts +43 -0
  154. package/src/store/chat/selectors/chat.ts +119 -0
  155. package/src/store/chat/selectors/index.ts +2 -0
  156. package/src/store/chat/selectors/topic.ts +28 -0
  157. package/src/store/chat/store.ts +63 -0
  158. package/src/store/files/slices/images/action.ts +6 -4
  159. package/src/store/global/store.ts +1 -1
  160. package/src/store/middleware/createHyperStorage/index.ts +40 -24
  161. package/src/store/middleware/createHyperStorage/keyMapper.ts +8 -2
  162. package/src/store/middleware/createHyperStorage/type.ts +10 -8
  163. package/src/store/plugin/slices/plugin/action.ts +1 -1
  164. package/src/store/plugin/store.ts +2 -4
  165. package/src/store/session/helpers.ts +1 -1
  166. package/src/store/session/hooks/useSessionHydrated.ts +6 -10
  167. package/src/store/session/initialState.ts +26 -7
  168. package/src/store/session/selectors.ts +1 -2
  169. package/src/store/session/slices/agent/action.ts +26 -35
  170. package/src/store/session/slices/agent/selectors.ts +32 -37
  171. package/src/store/session/slices/session/action.ts +81 -88
  172. package/src/store/session/slices/session/helpers.ts +19 -0
  173. package/src/store/session/slices/session/initialState.ts +7 -10
  174. package/src/store/session/slices/session/selectors/export.ts +12 -9
  175. package/src/store/session/slices/session/selectors/index.ts +2 -35
  176. package/src/store/session/slices/session/selectors/list.test.ts +25 -203
  177. package/src/store/session/slices/session/selectors/list.ts +40 -78
  178. package/src/store/session/store.ts +11 -17
  179. package/src/types/agent/index.ts +61 -0
  180. package/src/types/chatMessage.ts +11 -20
  181. package/src/types/exportConfig.ts +7 -2
  182. package/src/types/files.ts +2 -2
  183. package/src/types/llm.ts +1 -1
  184. package/src/types/meta.ts +24 -12
  185. package/src/types/openai/chat.ts +9 -1
  186. package/src/types/openai/functionCall.ts +4 -0
  187. package/src/types/session.ts +10 -94
  188. package/src/types/translate.ts +2 -1
  189. package/src/utils/config.ts +5 -11
  190. package/src/utils/fetch.ts +8 -2
  191. package/src/app/chat/features/Conversation/usePluginsInit.ts +0 -14
  192. package/src/database/core/schema.ts +0 -9
  193. package/src/helpers/export.ts +0 -69
  194. package/src/hooks/useExportConfig.ts +0 -6
  195. package/src/store/session/slices/chat/actions/index.ts +0 -32
  196. package/src/store/session/slices/chat/actions/topic.ts +0 -171
  197. package/src/store/session/slices/chat/actions/translate.ts +0 -113
  198. package/src/store/session/slices/chat/helpers.ts +0 -9
  199. package/src/store/session/slices/chat/initialState.ts +0 -13
  200. package/src/store/session/slices/chat/reducers/message.test.ts +0 -319
  201. package/src/store/session/slices/chat/reducers/message.ts +0 -156
  202. package/src/store/session/slices/chat/reducers/topic.test.ts +0 -179
  203. package/src/store/session/slices/chat/reducers/topic.ts +0 -81
  204. package/src/store/session/slices/chat/selectors/chat.ts +0 -111
  205. package/src/store/session/slices/chat/selectors/index.ts +0 -26
  206. package/src/store/session/slices/chat/selectors/topic.ts +0 -30
  207. package/src/store/session/slices/chat/selectors/utils.test.ts +0 -385
  208. package/src/store/session/slices/chat/selectors/utils.ts +0 -91
  209. package/src/store/session/slices/chat/utils.ts +0 -13
  210. package/src/store/session/slices/session/reducers/session.test.ts +0 -577
  211. package/src/store/session/slices/session/reducers/session.ts +0 -182
  212. package/src/types/database/db.ts +0 -4
  213. /package/src/store/{session/slices/chat → chat}/reducers/files.test.ts +0 -0
  214. /package/src/store/{session/slices/chat → chat}/reducers/files.ts +0 -0
@@ -0,0 +1,196 @@
1
+ import { BaseModel } from '@/database/core';
2
+ import { DB_Topic, DB_TopicSchema } from '@/database/schemas/topic';
3
+ import { ChatTopic } from '@/types/topic';
4
+ import { nanoid } from '@/utils/uuid';
5
+
6
+ export interface CreateTopicParams {
7
+ favorite?: boolean;
8
+ sessionId: string;
9
+ title: string;
10
+ }
11
+
12
+ export interface QueryTopicParams {
13
+ current?: number;
14
+ pageSize?: number;
15
+ sessionId: string;
16
+ }
17
+
18
+ class _TopicModel extends BaseModel {
19
+ constructor() {
20
+ super('topics', DB_TopicSchema);
21
+ }
22
+
23
+ async create({ title, favorite, sessionId }: CreateTopicParams, id = nanoid()) {
24
+ return this._add({ favorite: favorite ? 1 : 0, sessionId, title: title }, id);
25
+ }
26
+
27
+ async batchCreate(topics: CreateTopicParams[]) {
28
+ return this._batchAdd(topics.map((t) => ({ ...t, favorite: t.favorite ? 1 : 0 })));
29
+ }
30
+
31
+ async query({ pageSize = 9999, current = 0, sessionId }: QueryTopicParams): Promise<ChatTopic[]> {
32
+ const offset = current * pageSize;
33
+
34
+ // get all topics
35
+ const allTopics = await this.table.where('sessionId').equals(sessionId).toArray();
36
+
37
+ // 将所有主题按星标消息优先,时间倒序进行排序
38
+ const sortedTopics = allTopics.sort((a, b) => {
39
+ if (a.favorite && !b.favorite) return -1; // a是星标,b不是,a排前面
40
+ if (!a.favorite && b.favorite) return 1; // b是星标,a不是,b排前面
41
+
42
+ // 如果星标状态相同,则按时间倒序排序
43
+ return b.createdAt - a.createdAt;
44
+ });
45
+
46
+ // handle pageSize
47
+ const pagedTopics = sortedTopics.slice(offset, offset + pageSize);
48
+
49
+ return pagedTopics.map((i) => ({ ...i, favorite: !!i.favorite }));
50
+ }
51
+
52
+ async findBySessionId(sessionId: string) {
53
+ return this.table.where({ sessionId }).toArray();
54
+ }
55
+
56
+ async findById(id: string) {
57
+ return this.table.get(id);
58
+ }
59
+
60
+ /**
61
+ * Deletes a topic and all messages associated with it.
62
+ */
63
+ async delete(id: string) {
64
+ return this.db.transaction('rw', [this.table, this.db.messages], async () => {
65
+ // Delete all messages associated with the topic
66
+ const messages = await this.db.messages.where('topicId').equals(id).toArray();
67
+
68
+ if (messages.length > 0) {
69
+ const messageIds = messages.map((msg) => msg.id);
70
+ await this.db.messages.bulkDelete(messageIds);
71
+ }
72
+
73
+ await this.table.delete(id);
74
+ });
75
+ }
76
+
77
+ /**
78
+ * Deletes multiple topic based on the sessionId.
79
+ *
80
+ * @param {string} sessionId - The identifier of the assistant associated with the messages.
81
+ * @returns {Promise<void>}
82
+ */
83
+ async batchDeleteBySessionId(sessionId: string): Promise<void> {
84
+ // use sessionId as the filter criteria in the query.
85
+ const query = this.table.where('sessionId').equals(sessionId);
86
+
87
+ // Retrieve a collection of message IDs that satisfy the criteria
88
+ const topicIds = await query.primaryKeys();
89
+
90
+ // Use the bulkDelete method to delete all selected messages in bulk
91
+ return this.table.bulkDelete(topicIds);
92
+ }
93
+
94
+ async clearTable() {
95
+ return this.table.clear();
96
+ }
97
+
98
+ async update(id: string, data: Partial<DB_Topic>) {
99
+ return super._update(id, { ...data, updatedAt: Date.now() });
100
+ }
101
+
102
+ async toggleFavorite(id: string, newState?: boolean) {
103
+ const topic = await this.findById(id);
104
+ if (!topic) {
105
+ throw new Error(`Topic with id ${id} not found`);
106
+ }
107
+
108
+ // Toggle the 'favorite' status
109
+ const nextState = typeof newState !== 'undefined' ? newState : !topic.favorite;
110
+
111
+ await this.update(id, { favorite: nextState ? 1 : 0 });
112
+
113
+ return nextState;
114
+ }
115
+
116
+ /**
117
+ * Deletes multiple topics and all messages associated with them in a transaction.
118
+ */
119
+ async batchDelete(topicIds: string[]) {
120
+ return this.db.transaction('rw', [this.table, this.db.messages], async () => {
121
+ // Iterate over each topicId and delete related messages, then delete the topic itself
122
+ for (const topicId of topicIds) {
123
+ // Delete all messages associated with the topic
124
+ const messages = await this.db.messages.where('topicId').equals(topicId).toArray();
125
+ if (messages.length > 0) {
126
+ const messageIds = messages.map((msg) => msg.id);
127
+ await this.db.messages.bulkDelete(messageIds);
128
+ }
129
+
130
+ // Delete the topic
131
+ await this.table.delete(topicId);
132
+ }
133
+ });
134
+ }
135
+
136
+ queryAll() {
137
+ return this.table.orderBy('updatedAt').toArray();
138
+ }
139
+
140
+ /**
141
+ * Query topics by keyword in title, message content, or translated content
142
+ * @param keyword The keyword to search for
143
+ */
144
+ async queryByKeyword(keyword: string): Promise<ChatTopic[]> {
145
+ if (!keyword) return [];
146
+
147
+ console.time('queryTopicsByKeyword');
148
+ const keywordLowerCase = keyword.toLowerCase();
149
+
150
+ // Find topics with matching title
151
+ const matchingTopicsPromise = this.table
152
+ .filter((topic) => topic.title.toLowerCase().includes(keywordLowerCase))
153
+ .toArray();
154
+
155
+ // Find messages with matching content or translate.content
156
+ const matchingMessagesPromise = this.db.messages
157
+ .filter((message) => {
158
+ // check content
159
+ if (message.content.toLowerCase().includes(keywordLowerCase)) return true;
160
+
161
+ // check translate content
162
+ if (message.translate && message.translate.content) {
163
+ return message.translate.content.toLowerCase().includes(keywordLowerCase);
164
+ }
165
+
166
+ return false;
167
+ })
168
+ .toArray();
169
+
170
+ // Resolve both promises
171
+ const [matchingTopics, matchingMessages] = await Promise.all([
172
+ matchingTopicsPromise,
173
+ matchingMessagesPromise,
174
+ ]);
175
+
176
+ // Extract topic IDs from messages
177
+ const topicIdsFromMessages = matchingMessages.map((message) => message.topicId);
178
+
179
+ // Combine topic IDs from both sources
180
+ const combinedTopicIds = new Set([
181
+ ...topicIdsFromMessages,
182
+ ...matchingTopics.map((topic) => topic.id),
183
+ ]);
184
+
185
+ // Retrieve unique topics by IDs
186
+ const uniqueTopics = await this.table
187
+ .where('id')
188
+ .anyOf([...combinedTopicIds])
189
+ .toArray();
190
+
191
+ console.timeEnd('queryTopicsByKeyword');
192
+ return uniqueTopics.map((i) => ({ ...i, favorite: !!i.favorite }));
193
+ }
194
+ }
195
+
196
+ export const TopicModel = new _TopicModel();
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
 
3
- export const LocalFileSchema = z.object({
3
+ export const DB_FileSchema = z.object({
4
4
  /**
5
5
  * create Time
6
6
  */
@@ -35,4 +35,4 @@ export const LocalFileSchema = z.object({
35
35
  url: z.string().url().optional(),
36
36
  });
37
37
 
38
- export type LocalFile = z.infer<typeof LocalFileSchema>;
38
+ export type DB_File = z.infer<typeof DB_FileSchema>;
@@ -0,0 +1,37 @@
1
+ /* eslint-disable sort-keys-fix/sort-keys-fix */
2
+ import { z } from 'zod';
3
+
4
+ const TranslateSchema = z.object({
5
+ from: z.string().optional(),
6
+ to: z.string(),
7
+ content: z.string().optional(),
8
+ });
9
+
10
+ const PluginSchema = z.object({
11
+ identifier: z.string(),
12
+ arguments: z.string(),
13
+ apiName: z.string(),
14
+ type: z.enum(['default', 'standalone']).default('default'),
15
+ });
16
+
17
+ export const DB_MessageSchema = z.object({
18
+ role: z.enum(['user', 'system', 'assistant', 'function']),
19
+ content: z.string(),
20
+ files: z.array(z.string()).optional(),
21
+ favorite: z.number().int().min(0).max(1).optional(),
22
+ error: z.any().optional(),
23
+
24
+ plugin: PluginSchema.optional(),
25
+ pluginState: z.any().optional(),
26
+ fromModel: z.string().optional(),
27
+ translate: TranslateSchema.optional().or(z.null()),
28
+ tts: z.any().optional(),
29
+
30
+ // foreign keys
31
+ parentId: z.string().optional(),
32
+ quotaId: z.string().optional(),
33
+ sessionId: z.string(),
34
+ topicId: z.string().optional(),
35
+ });
36
+
37
+ export type DB_Message = z.infer<typeof DB_MessageSchema>;
@@ -0,0 +1,56 @@
1
+ import { z } from 'zod';
2
+
3
+ import { LobeMetaDataSchema } from '@/types/meta';
4
+
5
+ const fewShotsSchema = z.array(
6
+ z.object({
7
+ content: z.string(),
8
+ role: z.string(),
9
+ }),
10
+ );
11
+
12
+ const ttsSchema = z.object({
13
+ showAllLocaleVoice: z.boolean().optional(),
14
+ sttLocale: z.string().default('auto'),
15
+ ttsService: z.string().default('openai'),
16
+ voice: z
17
+ .object({
18
+ edge: z.string().optional(),
19
+ microsoft: z.string().optional(),
20
+ openai: z.string().default(''),
21
+ })
22
+ .optional(),
23
+ });
24
+
25
+ export const AgentSchema = z.object({
26
+ autoCreateTopicThreshold: z.number().default(2),
27
+ compressThreshold: z.number().optional(),
28
+ displayMode: z.enum(['chat', 'docs']).optional(),
29
+ enableAutoCreateTopic: z.boolean().default(true),
30
+ enableCompressThreshold: z.boolean().optional(),
31
+ enableHistoryCount: z.boolean().optional(),
32
+ enableMaxTokens: z.boolean().optional(),
33
+ fewShots: fewShotsSchema.optional(),
34
+ historyCount: z.number().default(8).optional(),
35
+ inputTemplate: z.string().optional(),
36
+ model: z.string().default('gpt-3.5-turbo'),
37
+ params: z.object({
38
+ frequency_penalty: z.number().default(0).optional(),
39
+ max_tokens: z.number().optional(),
40
+ presence_penalty: z.number().default(0).optional(),
41
+ temperature: z.number().default(0.6).optional(),
42
+ top_p: z.number().default(1).optional(),
43
+ }),
44
+ plugins: z.array(z.string()).optional(),
45
+ systemRole: z.string().default(''),
46
+ tts: ttsSchema.optional(),
47
+ });
48
+
49
+ export const DB_SessionSchema = z.object({
50
+ config: AgentSchema,
51
+ group: z.string().default('default'),
52
+ meta: LobeMetaDataSchema,
53
+ type: z.enum(['agent', 'group']).default('agent'),
54
+ });
55
+
56
+ export type DB_Session = z.infer<typeof DB_SessionSchema>;
@@ -0,0 +1,12 @@
1
+ /* eslint-disable sort-keys-fix/sort-keys-fix */
2
+ import { z } from 'zod';
3
+
4
+ export const DB_TopicSchema = z.object({
5
+ title: z.string(),
6
+ favorite: z.number().int().default(0),
7
+
8
+ // foreign keys
9
+ sessionId: z.string().optional(),
10
+ });
11
+
12
+ export type DB_Topic = z.infer<typeof DB_TopicSchema>;
@@ -7,8 +7,8 @@ import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
9
  import { useTTS } from '@/hooks/useTTS';
10
+ import { TTSServer } from '@/types/agent';
10
11
  import { ChatMessageError } from '@/types/chatMessage';
11
- import { TTSServer } from '@/types/session';
12
12
  import { getMessageError } from '@/utils/fetch';
13
13
 
14
14
  interface SelectWithTTSPreviewProps extends SelectProps {
@@ -5,8 +5,8 @@ import { chainSummaryAgentName } from '@/chains/summaryAgentName';
5
5
  import { chainSummaryDescription } from '@/chains/summaryDescription';
6
6
  import { chainSummaryTags } from '@/chains/summaryTags';
7
7
  import { chatService } from '@/services/chat';
8
+ import { LobeAgentConfig } from '@/types/agent';
8
9
  import { MetaData } from '@/types/meta';
9
- import { LobeAgentConfig } from '@/types/session';
10
10
  import { setNamespace } from '@/utils/storeDebug';
11
11
 
12
12
  import { SessionLoadingState } from '../store/initialState';
@@ -1,7 +1,7 @@
1
1
  import { DEFAULT_AGENT_META } from '@/const/meta';
2
2
  import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
3
3
  import { MetaData } from '@/types/meta';
4
- import { LobeAgentConfig } from '@/types/session';
4
+ import { LobeAgentConfig } from '@/types/agent';
5
5
 
6
6
  export interface State {
7
7
  autocompleteLoading: SessionLoadingState;
@@ -1,7 +1,7 @@
1
1
  import { produce } from 'immer';
2
2
 
3
3
  import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
4
- import { LobeAgentConfig } from '@/types/session';
4
+ import { LobeAgentConfig } from '@/types/agent';
5
5
  import { merge } from '@/utils/merge';
6
6
 
7
7
  export type ConfigDispatch =
@@ -0,0 +1,240 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { Button, Result, Table, Upload } from 'antd';
3
+ import { createStyles } from 'antd-style';
4
+ import { BadgeCheck, ImportIcon } from 'lucide-react';
5
+ import React, { ReactNode, memo, useState } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { Center, Flexbox } from 'react-layout-kit';
8
+
9
+ import DataStyleModal from '@/components/DataStyleModal';
10
+ import { useImportConfig } from '@/hooks/useImportConfig';
11
+ import { ImportResult, ImportResults } from '@/services/config';
12
+
13
+ const useStyles = createStyles(({ css, token }) => {
14
+ const size = 28;
15
+
16
+ return {
17
+ loader: css`
18
+ transform: translateX(-${size * 2}px);
19
+
20
+ aspect-ratio: 1;
21
+ width: 6px;
22
+
23
+ color: ${token.colorPrimary};
24
+
25
+ border-radius: 50%;
26
+ box-shadow:
27
+ ${size}px -${size}px 0 0,
28
+ ${size * 2}px -${size}px 0 0,
29
+ ${size * 3}px -${size}px 0 0,
30
+ ${size}px 0 0 5px,
31
+ ${size * 2}px 0 0 5px,
32
+ ${size * 3}px 0 0 5px,
33
+ ${size}px ${size}px 0 0,
34
+ ${size * 2}px ${size}px 0 0,
35
+ ${size * 3}px ${size}px 0 0;
36
+
37
+ animation: loading 2s infinite linear;
38
+
39
+ @keyframes loading {
40
+ 12.5% {
41
+ box-shadow:
42
+ ${size}px -${size}px 0 0,
43
+ ${size * 2}px -${size}px 0 0,
44
+ ${size * 3}px -${size}px 0 5px,
45
+ ${size}px 0 0 5px,
46
+ ${size * 2}px 0 0 0,
47
+ ${size * 3}px 0 0 5px,
48
+ ${size}px ${size}px 0 0,
49
+ ${size * 2}px ${size}px 0 0,
50
+ ${size * 3}px ${size}px 0 0;
51
+ }
52
+
53
+ 25% {
54
+ box-shadow:
55
+ ${size}px -${size}px 0 5px,
56
+ ${size * 2}px -${size}px 0 0,
57
+ ${size * 3}px -${size}px 0 5px,
58
+ ${size}px 0 0 0,
59
+ ${size * 2}px 0 0 0,
60
+ ${size * 3}px 0 0 0,
61
+ ${size}px ${size}px 0 0,
62
+ ${size * 2}px ${size}px 0 5px,
63
+ ${size * 3}px ${size}px 0 0;
64
+ }
65
+
66
+ 50% {
67
+ box-shadow:
68
+ ${size}px -${size}px 0 5px,
69
+ ${size * 2}px -${size}px 0 5px,
70
+ ${size * 3}px -${size}px 0 0,
71
+ ${size}px 0 0 0,
72
+ ${size * 2}px 0 0 0,
73
+ ${size * 3}px 0 0 0,
74
+ ${size}px ${size}px 0 0,
75
+ ${size * 2}px ${size}px 0 0,
76
+ ${size * 3}px ${size}px 0 5px;
77
+ }
78
+
79
+ 62.5% {
80
+ box-shadow:
81
+ ${size}px -${size}px 0 0,
82
+ ${size * 2}px -${size}px 0 0,
83
+ ${size * 3}px -${size}px 0 0,
84
+ ${size}px 0 0 5px,
85
+ ${size * 2}px 0 0 0,
86
+ ${size * 3}px 0 0 0,
87
+ ${size}px ${size}px 0 0,
88
+ ${size * 2}px ${size}px 0 5px,
89
+ ${size * 3}px ${size}px 0 5px;
90
+ }
91
+
92
+ 75% {
93
+ box-shadow:
94
+ ${size}px -${size}px 0 0,
95
+ ${size * 2}px -${size}px 0 5px,
96
+ ${size * 3}px -${size}px 0 0,
97
+ ${size}px 0 0 0,
98
+ ${size * 2}px 0 0 0,
99
+ ${size * 3}px 0 0 5px,
100
+ ${size}px ${size}px 0 0,
101
+ ${size * 2}px ${size}px 0 0,
102
+ ${size * 3}px ${size}px 0 5px;
103
+ }
104
+
105
+ 87.5% {
106
+ box-shadow:
107
+ ${size}px -${size}px 0 0,
108
+ ${size * 2}px -${size}px 0 5px,
109
+ ${size * 3}px -${size}px 0 0,
110
+ ${size}px 0 0 0,
111
+ ${size * 2}px 0 0 5px,
112
+ ${size * 3}px 0 0 0,
113
+ ${size}px ${size}px 0 5px,
114
+ ${size * 2}px ${size}px 0 0,
115
+ ${size * 3}px ${size}px 0 0;
116
+ }
117
+ }
118
+ `,
119
+ };
120
+ });
121
+
122
+ enum ImportState {
123
+ Start,
124
+ Loading,
125
+ Finished,
126
+ Close,
127
+ }
128
+
129
+ interface DataImporterProps {
130
+ children?: ReactNode;
131
+ onFinishImport?: () => void;
132
+ }
133
+ const DataImporter = memo<DataImporterProps>(({ children, onFinishImport }) => {
134
+ const { t } = useTranslation('common');
135
+ const { importConfig } = useImportConfig();
136
+ const [duration, setDuration] = useState(0);
137
+ const [importState, setImportState] = useState(ImportState.Start);
138
+ const [importData, setImportData] = useState<ImportResults | undefined>();
139
+ const { styles } = useStyles();
140
+
141
+ return (
142
+ <>
143
+ <DataStyleModal
144
+ icon={ImportIcon}
145
+ open={importState === ImportState.Loading || importState === ImportState.Finished}
146
+ title={t('importModal.title')}
147
+ width={importState === ImportState.Finished ? 500 : 400}
148
+ >
149
+ <Center
150
+ gap={24}
151
+ padding={40}
152
+ style={{
153
+ paddingBlock: importState === ImportState.Finished ? 0 : undefined,
154
+ }}
155
+ >
156
+ {importState === ImportState.Finished ? (
157
+ // TODO: 在这里加个撒花效果
158
+ <Result
159
+ extra={
160
+ <Button
161
+ onClick={() => {
162
+ setImportState(ImportState.Close);
163
+
164
+ onFinishImport?.();
165
+ }}
166
+ size={'large'}
167
+ type={'primary'}
168
+ >
169
+ {t('importModal.finish.start')}
170
+ </Button>
171
+ }
172
+ icon={<Icon icon={BadgeCheck} />}
173
+ status={'success'}
174
+ style={{ paddingBlock: 24 }}
175
+ subTitle={
176
+ // if there is no importData, means it's only import the settings
177
+ !importData ? (
178
+ t('importModal.finish.onlySettings')
179
+ ) : (
180
+ <Flexbox gap={16} width={400}>
181
+ {t('importModal.finish.subTitle', { duration: (duration / 1000).toFixed(2) })}
182
+ <Table
183
+ bordered
184
+ columns={[
185
+ { dataIndex: 'title', title: t('importModal.result.type') },
186
+ { dataIndex: 'added', title: t('importModal.result.added') },
187
+ { dataIndex: 'skips', title: t('importModal.result.skips') },
188
+ { dataIndex: 'error', title: t('importModal.result.errors') },
189
+ ]}
190
+ dataSource={Object.entries(importData).map(
191
+ ([item, value]: [string, ImportResult]) => ({
192
+ added: value.added,
193
+ error: value.errors,
194
+ skips: value.skips,
195
+ title: t(`importModal.result.${item as keyof ImportResults}`),
196
+ }),
197
+ )}
198
+ pagination={false}
199
+ size={'small'}
200
+ />
201
+ </Flexbox>
202
+ )
203
+ }
204
+ title={t('importModal.finish.title')}
205
+ />
206
+ ) : (
207
+ <>
208
+ <Center style={{ height: 80 }}>
209
+ <div className={styles.loader} />
210
+ </Center>
211
+ <p>{t('importModal.loading')}</p>
212
+ </>
213
+ )}
214
+ </Center>
215
+ </DataStyleModal>
216
+ <Upload
217
+ beforeUpload={async (file) => {
218
+ setImportState(ImportState.Loading);
219
+ const time = Date.now();
220
+ const data = await importConfig(file);
221
+
222
+ if (data) {
223
+ setImportData(data);
224
+ }
225
+
226
+ setDuration(Date.now() - time);
227
+ setImportState(ImportState.Finished);
228
+
229
+ return false;
230
+ }}
231
+ maxCount={1}
232
+ showUploadList={false}
233
+ >
234
+ {children}
235
+ </Upload>
236
+ </>
237
+ );
238
+ });
239
+
240
+ export default DataImporter;
@@ -0,0 +1,34 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { App, FloatButton, Spin } from 'antd';
3
+ import { DatabaseIcon, Loader2 } from 'lucide-react';
4
+ import { memo, useState } from 'react';
5
+
6
+ import { debugService } from '@/services/debug';
7
+
8
+ const DebugUI = memo(() => {
9
+ const [loading, setLoading] = useState(false);
10
+ const { message } = App.useApp();
11
+ return (
12
+ <>
13
+ {loading && <Spin fullscreen />}
14
+ <FloatButton
15
+ icon={<Icon icon={loading ? Loader2 : DatabaseIcon} spin={loading} />}
16
+ onClick={async () => {
17
+ setLoading(true);
18
+
19
+ const startTime = Date.now();
20
+
21
+ await debugService.insertLargeDataToDB();
22
+
23
+ const duration = Date.now() - startTime;
24
+
25
+ setLoading(false);
26
+ message.success(`插入成功,耗时:${(duration / 1000).toFixed(1)} s`);
27
+ }}
28
+ tooltip={'性能压测,插入100w数据'}
29
+ />
30
+ </>
31
+ );
32
+ });
33
+
34
+ export default DebugUI;