@lobehub/chat 0.120.5 → 0.121.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 (122) hide show
  1. package/.github/ISSUE_TEMPLATE/2_feature_request_cn.yml +1 -1
  2. package/CHANGELOG.md +50 -0
  3. package/README.md +9 -9
  4. package/README.zh-CN.md +9 -9
  5. package/docs/Development/Feature-Development-Frontend.zh-CN.md +126 -0
  6. package/docs/Development/Feature-Development.zh-CN.md +664 -83
  7. package/docs/Development/{State-Management/State-Management-Intro.zh-CN.md → State-Management-Intro.zh-CN.md} +9 -9
  8. package/docs/Home.md +1 -0
  9. package/docs/_Sidebar.md +1 -0
  10. package/locales/ar/chat.json +13 -1
  11. package/locales/ar/common.json +1 -0
  12. package/locales/de-DE/chat.json +13 -1
  13. package/locales/de-DE/common.json +1 -0
  14. package/locales/en-US/chat.json +13 -1
  15. package/locales/en-US/common.json +1 -0
  16. package/locales/es-ES/chat.json +13 -1
  17. package/locales/es-ES/common.json +1 -0
  18. package/locales/fr-FR/chat.json +13 -1
  19. package/locales/fr-FR/common.json +1 -0
  20. package/locales/it-IT/chat.json +13 -1
  21. package/locales/it-IT/common.json +1 -0
  22. package/locales/ja-JP/chat.json +13 -1
  23. package/locales/ja-JP/common.json +1 -0
  24. package/locales/ko-KR/chat.json +13 -1
  25. package/locales/ko-KR/common.json +1 -0
  26. package/locales/nl-NL/chat.json +13 -1
  27. package/locales/nl-NL/common.json +1 -0
  28. package/locales/pl-PL/chat.json +13 -1
  29. package/locales/pl-PL/common.json +1 -0
  30. package/locales/pt-BR/chat.json +13 -1
  31. package/locales/pt-BR/common.json +1 -0
  32. package/locales/ru-RU/chat.json +13 -1
  33. package/locales/ru-RU/common.json +1 -0
  34. package/locales/tr-TR/chat.json +13 -1
  35. package/locales/tr-TR/common.json +1 -0
  36. package/locales/vi-VN/chat.json +13 -1
  37. package/locales/vi-VN/common.json +1 -0
  38. package/locales/zh-CN/chat.json +13 -1
  39. package/locales/zh-CN/common.json +1 -0
  40. package/locales/zh-TW/chat.json +13 -1
  41. package/locales/zh-TW/common.json +1 -0
  42. package/package.json +4 -6
  43. package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +18 -5
  44. package/src/app/chat/(desktop)/features/ChatInput/index.tsx +1 -1
  45. package/src/app/chat/features/ChatHeader/ShareButton/index.tsx +0 -1
  46. package/src/app/chat/features/ChatHeader/ShareButton/style.ts +0 -1
  47. package/src/app/chat/features/Migration/Failed.tsx +2 -0
  48. package/src/app/chat/features/SessionListContent/CollapseGroup/Actions.tsx +115 -0
  49. package/src/app/chat/features/SessionListContent/CollapseGroup/index.tsx +6 -0
  50. package/src/app/chat/features/SessionListContent/DefaultMode.tsx +63 -23
  51. package/src/app/chat/features/SessionListContent/List/AddButton.tsx +3 -3
  52. package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +89 -35
  53. package/src/app/chat/features/SessionListContent/List/Item/index.tsx +35 -15
  54. package/src/app/chat/features/SessionListContent/List/index.tsx +4 -2
  55. package/src/app/chat/features/SessionListContent/Modals/ConfigGroupModal/GroupItem.tsx +84 -0
  56. package/src/app/chat/features/SessionListContent/Modals/ConfigGroupModal/index.tsx +79 -0
  57. package/src/app/chat/features/SessionListContent/Modals/CreateGroupModal.tsx +63 -0
  58. package/src/app/chat/features/SessionListContent/Modals/RenameGroupModal.tsx +50 -0
  59. package/src/app/chat/features/SessionListContent/SearchMode.tsx +1 -1
  60. package/src/app/settings/common/Common.tsx +5 -1
  61. package/src/components/FullscreenLoading/index.tsx +1 -1
  62. package/src/database/core/__tests__/db-upgrade.test.ts +42 -0
  63. package/src/database/core/db.ts +26 -4
  64. package/src/database/core/schemas.ts +14 -1
  65. package/src/database/core/types/db.ts +2 -0
  66. package/src/database/models/__tests__/message.test.ts +42 -0
  67. package/src/database/models/__tests__/plugin.test.ts +77 -0
  68. package/src/database/models/__tests__/session.test.ts +202 -63
  69. package/src/database/models/__tests__/sessionGroup.test.ts +195 -0
  70. package/src/database/models/__tests__/topic.test.ts +76 -1
  71. package/src/database/models/message.ts +7 -8
  72. package/src/database/models/session.ts +103 -7
  73. package/src/database/models/sessionGroup.ts +76 -0
  74. package/src/database/schemas/session.ts +1 -0
  75. package/src/database/schemas/sessionGroup.ts +8 -0
  76. package/src/features/ChatInput/ActionBar/Clear.tsx +2 -1
  77. package/src/features/Conversation/Actions/Assistant.tsx +1 -1
  78. package/src/features/Conversation/Actions/Function.tsx +2 -2
  79. package/src/features/Conversation/Actions/User.tsx +1 -1
  80. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +3 -3
  81. package/src/features/PluginDevModal/index.tsx +3 -0
  82. package/src/features/PluginStore/PluginItem/Action.tsx +2 -0
  83. package/src/locales/default/chat.ts +13 -1
  84. package/src/locales/default/common.ts +1 -0
  85. package/src/migrations/FromV2ToV3/fixtures/input-v2-session.json +72 -0
  86. package/src/migrations/FromV2ToV3/fixtures/output-v3-from-v1.json +203 -0
  87. package/src/migrations/FromV2ToV3/fixtures/output-v3.json +74 -0
  88. package/src/migrations/FromV2ToV3/index.ts +30 -0
  89. package/src/migrations/FromV2ToV3/migrations.test.ts +42 -0
  90. package/src/migrations/FromV2ToV3/types/v3.ts +27 -0
  91. package/src/migrations/index.ts +7 -1
  92. package/src/services/__tests__/session.test.ts +198 -7
  93. package/src/services/config.ts +23 -7
  94. package/src/services/session.ts +67 -14
  95. package/src/store/global/slices/common/action.test.ts +130 -0
  96. package/src/store/global/slices/common/action.ts +14 -0
  97. package/src/store/global/slices/common/initialState.ts +6 -2
  98. package/src/store/global/slices/common/selectors.ts +1 -4
  99. package/src/store/global/slices/settings/__snapshots__/selectors.test.ts.snap +83 -0
  100. package/src/store/global/slices/settings/action.ts +2 -2
  101. package/src/store/global/slices/settings/selectors.test.ts +132 -182
  102. package/src/store/global/store.ts +7 -0
  103. package/src/store/session/initialState.ts +5 -30
  104. package/src/store/session/selectors.ts +2 -1
  105. package/src/store/session/slices/session/action.test.ts +56 -0
  106. package/src/store/session/slices/session/action.ts +13 -9
  107. package/src/store/session/slices/session/helpers.ts +2 -3
  108. package/src/store/session/slices/session/initialState.ts +31 -23
  109. package/src/store/session/slices/session/selectors/index.ts +0 -1
  110. package/src/store/session/slices/session/selectors/list.ts +11 -18
  111. package/src/store/session/slices/sessionGroup/action.ts +54 -0
  112. package/src/store/session/slices/sessionGroup/initialState.ts +5 -0
  113. package/src/store/session/slices/sessionGroup/selectors.ts +15 -0
  114. package/src/store/session/store.ts +9 -2
  115. package/src/styles/antdOverride.ts +0 -7
  116. package/src/types/exportConfig.ts +3 -1
  117. package/src/types/session.ts +27 -3
  118. package/src/store/session/slices/session/selectors/export.ts +0 -31
  119. /package/docs/Development/{Feature-Development.md → Feature-Development-Frontend.md} +0 -0
  120. /package/docs/Development/{State-Management/State-Management-Intro.md → State-Management-Intro.md} +0 -0
  121. /package/docs/Development/{State-Management/Selectors.md → State-Management-Selectors.md} +0 -0
  122. /package/docs/Development/{State-Management/Selectors.zh-CN.md → State-Management-Selectors.zh-CN.md} +0 -0
@@ -25,6 +25,7 @@ describe('TopicModel', () => {
25
25
  // Clean up the database after each test
26
26
  await TopicModel.clearTable();
27
27
  });
28
+
28
29
  describe('create', () => {
29
30
  it('should create a topic record', async () => {
30
31
  const result = await TopicModel.create(topicData);
@@ -395,7 +396,7 @@ describe('TopicModel', () => {
395
396
  it('should fail if the database transaction fails', async () => {
396
397
  // 强制数据库事务失败,例如通过在复制过程中抛出异常
397
398
  const dbTransactionFailedError = new Error('DB transaction failed');
398
- vi.spyOn(TopicModel['db'], 'transaction').mockImplementation((async () => {
399
+ const spyOn = vi.spyOn(TopicModel['db'], 'transaction').mockImplementation((async () => {
399
400
  throw dbTransactionFailedError;
400
401
  }) as any);
401
402
 
@@ -403,6 +404,7 @@ describe('TopicModel', () => {
403
404
  await expect(TopicModel.duplicateTopic(originalTopic.id)).rejects.toThrow(
404
405
  dbTransactionFailedError,
405
406
  );
407
+ spyOn.mockRestore();
406
408
  });
407
409
 
408
410
  it('should not create partial duplicates if the process fails at some point', async () => {
@@ -422,4 +424,77 @@ describe('TopicModel', () => {
422
424
  expect(messages).toHaveLength(originalMessages.length); // 只有原始消息
423
425
  });
424
426
  });
427
+
428
+ describe('clearTable', () => {
429
+ it('should clear the table', async () => {
430
+ // Create a topic to ensure the table is not empty
431
+ await TopicModel.create(topicData);
432
+
433
+ // Clear the table
434
+ await TopicModel.clearTable();
435
+
436
+ // Verify the table is empty
437
+ const topics = await TopicModel.queryAll();
438
+ expect(topics).toHaveLength(0);
439
+ });
440
+ });
441
+
442
+ describe('update', () => {
443
+ it('should update a topic', async () => {
444
+ // Create a topic
445
+ const createdTopic = await TopicModel.create(topicData);
446
+
447
+ // Update the topic
448
+ const newTitle = 'Updated Title';
449
+ await TopicModel.update(createdTopic.id, { title: newTitle });
450
+
451
+ // Verify the topic is updated
452
+ const updatedTopic = await TopicModel.findById(createdTopic.id);
453
+ expect(updatedTopic.title).toBe(newTitle);
454
+ });
455
+ });
456
+
457
+ describe('batchDelete', () => {
458
+ it('should batch delete topics', async () => {
459
+ // Create multiple topics
460
+ const topic1 = await TopicModel.create(topicData);
461
+ const topic2 = await TopicModel.create(topicData);
462
+
463
+ await TopicModel.create(topicData);
464
+
465
+ const ids = [topic1.id, topic2.id];
466
+ // Batch delete the topics
467
+ await TopicModel.batchDelete(ids);
468
+
469
+ expect(await TopicModel.table.count()).toEqual(1);
470
+ });
471
+ });
472
+
473
+ describe('queryAll', () => {
474
+ it('should query all topics', async () => {
475
+ // Create multiple topics
476
+ await TopicModel.batchCreate([topicData, topicData]);
477
+
478
+ // Query all topics
479
+ const topics = await TopicModel.queryAll();
480
+
481
+ // Verify all topics are queried
482
+ expect(topics).toHaveLength(2);
483
+ });
484
+ });
485
+
486
+ describe('queryByKeyword', () => {
487
+ it('should query topics by keyword', async () => {
488
+ // Create a topic with a unique title
489
+ const uniqueTitle = 'Unique Title';
490
+ await TopicModel.create({ ...topicData, title: uniqueTitle });
491
+
492
+ // Query topics by the unique title
493
+ const topics = await TopicModel.queryByKeyword(uniqueTitle);
494
+
495
+ // Verify the correct topic is queried
496
+ expect(topics).toHaveLength(1);
497
+ expect(topics[0].title).toBe(uniqueTitle);
498
+ });
499
+ });
425
500
  });
@@ -132,7 +132,7 @@ class _MessageModel extends BaseModel {
132
132
 
133
133
  /**
134
134
  * Deletes multiple messages based on the assistantId and optionally the topicId.
135
- * If topicId is not provided, it deletes messages where topicId is undefined.
135
+ * If topicId is not provided, it deletes messages where topicId is undefined or null.
136
136
  * If topicId is provided, it deletes messages with that specific topicId.
137
137
  *
138
138
  * @param {string} sessionId - The identifier of the assistant associated with the messages.
@@ -142,13 +142,12 @@ class _MessageModel extends BaseModel {
142
142
  async batchDelete(sessionId: string, topicId: string | undefined): Promise<void> {
143
143
  // If topicId is specified, use both assistantId and topicId as the filter criteria in the query.
144
144
  // Otherwise, filter by assistantId and require that topicId is undefined.
145
- const query =
146
- topicId !== undefined
147
- ? this.table.where({ sessionId, topicId }) // Use a compound index
148
- : this.table
149
- .where('sessionId')
150
- .equals(sessionId)
151
- .and((message) => message.topicId === undefined);
145
+ const query = !!topicId
146
+ ? this.table.where({ sessionId, topicId }) // Use a compound index
147
+ : this.table
148
+ .where('sessionId')
149
+ .equals(sessionId)
150
+ .and((message) => !message.topicId);
152
151
 
153
152
  // Retrieve a collection of message IDs that satisfy the criteria
154
153
  const messageIds = await query.primaryKeys();
@@ -3,9 +3,16 @@ import { DeepPartial } from 'utility-types';
3
3
  import { DEFAULT_AGENT_LOBE_SESSION } from '@/const/session';
4
4
  import { BaseModel } from '@/database/core';
5
5
  import { DBModel } from '@/database/core/types/db';
6
+ import { SessionGroupModel } from '@/database/models/sessionGroup';
6
7
  import { DB_Session, DB_SessionSchema } from '@/database/schemas/session';
7
8
  import { LobeAgentConfig } from '@/types/agent';
8
- import { LobeAgentSession, LobeSessions, SessionGroupKey } from '@/types/session';
9
+ import {
10
+ ChatSessionList,
11
+ LobeAgentSession,
12
+ LobeSessions,
13
+ SessionDefaultGroup,
14
+ SessionGroupId,
15
+ } from '@/types/session';
9
16
  import { merge } from '@/utils/merge';
10
17
  import { uuid } from '@/utils/uuid';
11
18
 
@@ -20,7 +27,21 @@ class _SessionModel extends BaseModel {
20
27
  }
21
28
 
22
29
  async batchCreate(sessions: LobeAgentSession[]) {
23
- return this._batchAdd(sessions, { idGenerator: uuid });
30
+ const DB_Sessions = await Promise.all(
31
+ sessions.map(async (s) => {
32
+ if (s.group && s.group !== SessionDefaultGroup.Default) {
33
+ // Check if the group exists in the SessionGroup table
34
+ const groupExists = await SessionGroupModel.findById(s.group);
35
+ // If the group does not exist, set it to default
36
+ if (!groupExists) {
37
+ s.group = SessionDefaultGroup.Default;
38
+ }
39
+ }
40
+ return this.mapToDB_Session(s);
41
+ }),
42
+ );
43
+
44
+ return this._batchAdd<DB_Session>(DB_Sessions, { idGenerator: uuid });
24
45
  }
25
46
 
26
47
  async query({
@@ -29,21 +50,66 @@ class _SessionModel extends BaseModel {
29
50
  }: { current?: number; pageSize?: number } = {}): Promise<LobeSessions> {
30
51
  const offset = current * pageSize;
31
52
 
32
- return this.table.orderBy('updatedAt').reverse().offset(offset).limit(pageSize).toArray();
53
+ const items: DBModel<DB_Session>[] = await this.table
54
+ .orderBy('updatedAt')
55
+ .reverse()
56
+ .offset(offset)
57
+ .limit(pageSize)
58
+ .toArray();
59
+
60
+ return this.mapToAgentSessions(items);
61
+ }
62
+
63
+ async queryWithGroups(): Promise<ChatSessionList> {
64
+ const groups = await SessionGroupModel.query();
65
+ const customGroups = await this.queryByGroupIds(groups.map((item) => item.id));
66
+ const defaultItems = await this.querySessionsByGroupId(SessionDefaultGroup.Default);
67
+ const pinnedItems = await this.getPinnedSessions();
68
+
69
+ const all = await this.query();
70
+ return {
71
+ all,
72
+ customGroup: groups.map((group) => ({
73
+ ...group,
74
+ children: customGroups[group.id],
75
+ })),
76
+ default: defaultItems,
77
+ pinned: pinnedItems,
78
+ };
33
79
  }
34
80
 
35
81
  /**
36
82
  * get sessions by group
37
83
  * @param group
38
84
  */
39
- async queryByGroup(group: SessionGroupKey) {
40
- return this.table.where('group').equals(group).toArray();
85
+ async querySessionsByGroupId(group: SessionGroupId): Promise<LobeSessions> {
86
+ const items: DBModel<DB_Session>[] = await this.table
87
+ .where('group')
88
+ .equals(group)
89
+ .and((session) => !session.pinned)
90
+ .reverse()
91
+ .sortBy('updatedAt');
92
+
93
+ return this.mapToAgentSessions(items);
94
+ }
95
+
96
+ async queryByGroupIds(groups: string[]) {
97
+ const pools = groups.map(async (id) => {
98
+ return [id, await this.querySessionsByGroupId(id)] as const;
99
+ });
100
+ const groupItems = await Promise.all(pools);
101
+
102
+ return Object.fromEntries(groupItems);
41
103
  }
42
104
 
43
105
  async update(id: string, data: Partial<DB_Session>) {
44
106
  return super._update(id, data);
45
107
  }
46
108
 
109
+ async updatePinned(id: string, pinned: boolean) {
110
+ return this.update(id, { pinned: pinned ? 1 : 0 });
111
+ }
112
+
47
113
  async updateConfig(id: string, data: DeepPartial<LobeAgentConfig>) {
48
114
  const session = await this.findById(id);
49
115
  if (!session) return;
@@ -52,6 +118,7 @@ class _SessionModel extends BaseModel {
52
118
 
53
119
  return this.update(id, { config });
54
120
  }
121
+
55
122
  /**
56
123
  * Delete a session , also delete all messages and topic associated with it.
57
124
  */
@@ -148,13 +215,13 @@ class _SessionModel extends BaseModel {
148
215
  ]);
149
216
 
150
217
  // Retrieve unique sessions by IDs
151
- const data = await this.table
218
+ const items: DBModel<DB_Session>[] = await this.table
152
219
  .where('id')
153
220
  .anyOf([...combinedSessionIds])
154
221
  .toArray();
155
222
 
156
223
  console.timeEnd('queryByKeyword');
157
- return data;
224
+ return this.mapToAgentSessions(items);
158
225
  }
159
226
 
160
227
  async duplicate(id: string, newTitle?: string) {
@@ -165,6 +232,35 @@ class _SessionModel extends BaseModel {
165
232
 
166
233
  return this._add(newSession, uuid());
167
234
  }
235
+
236
+ async getPinnedSessions(): Promise<LobeSessions> {
237
+ const items: DBModel<DB_Session>[] = await this.table
238
+ .where('pinned')
239
+ .equals(1)
240
+ .reverse()
241
+ .sortBy('updatedAt');
242
+
243
+ return this.mapToAgentSessions(items);
244
+ }
245
+
246
+ private mapToDB_Session(session: LobeAgentSession): DBModel<DB_Session> {
247
+ return {
248
+ ...session,
249
+ group: session.group || SessionDefaultGroup.Default,
250
+ pinned: session.pinned ? 1 : 0,
251
+ };
252
+ }
253
+
254
+ private DB_SessionToAgentSession(session: DBModel<DB_Session>) {
255
+ return {
256
+ ...session,
257
+ pinned: !!session.pinned,
258
+ } as LobeAgentSession;
259
+ }
260
+
261
+ private mapToAgentSessions(session: DBModel<DB_Session>[]) {
262
+ return session.map((item) => this.DB_SessionToAgentSession(item));
263
+ }
168
264
  }
169
265
 
170
266
  export const SessionModel = new _SessionModel();
@@ -0,0 +1,76 @@
1
+ import { BaseModel } from '@/database/core';
2
+ import { DB_SessionGroup, DB_SessionGroupSchema } from '@/database/schemas/sessionGroup';
3
+ import { SessionGroups } from '@/types/session';
4
+ import { nanoid } from '@/utils/uuid';
5
+
6
+ class _SessionGroupModel extends BaseModel {
7
+ constructor() {
8
+ super('sessionGroups', DB_SessionGroupSchema);
9
+ }
10
+
11
+ async create(name: string, sort?: number, id = nanoid()) {
12
+ return this._add({ name, sort }, id);
13
+ }
14
+ async batchCreate(groups: SessionGroups) {
15
+ return this._batchAdd(groups, { idGenerator: nanoid });
16
+ }
17
+
18
+ async findById(id: string): Promise<DB_SessionGroup> {
19
+ return this.table.get(id);
20
+ }
21
+
22
+ async update(id: string, data: Partial<DB_SessionGroup>) {
23
+ return super._update(id, data);
24
+ }
25
+
26
+ async delete(id: string, removeGroupItem: boolean = false) {
27
+ this.db.sessions.toCollection().modify((session) => {
28
+ // update all session associated with the sessionGroup to default
29
+ if (session.group === id) session.group = 'default';
30
+ });
31
+ if (!removeGroupItem) {
32
+ return this.table.delete(id);
33
+ } else {
34
+ return this.db.sessions.where('group').equals(id).delete();
35
+ }
36
+ }
37
+
38
+ async query(): Promise<SessionGroups> {
39
+ const allGroups = await this.table.toArray();
40
+
41
+ // 自定义排序,先按 sort 存在与否分组,然后分别排序
42
+ return allGroups.sort((a, b) => {
43
+ // 如果两个项都有 sort,则按 sort 排序
44
+ if (a.sort !== undefined && b.sort !== undefined) {
45
+ // 如果sort 一样,按时间倒序排序
46
+ if (a.sort === b.sort) return b.createdAt - a.createdAt;
47
+
48
+ return a.sort - b.sort;
49
+ }
50
+ // 如果 a 有 sort 而 b 没有,则 a 排在前面
51
+ if (a.sort !== undefined) {
52
+ return -1;
53
+ }
54
+ // 如果 b 有 sort 而 a 没有,则 b 排在前面
55
+ if (b.sort !== undefined) {
56
+ return 1;
57
+ }
58
+ // 如果两个项都没有 sort,则按 createdAt 倒序排序
59
+ return b.createdAt - a.createdAt;
60
+ });
61
+ }
62
+
63
+ async updateOrder(sortMap: { id: string; sort: number }[]) {
64
+ return this.db.transaction('rw', this.table, async () => {
65
+ for (const { id, sort } of sortMap) {
66
+ await this.table.update(id, { sort });
67
+ }
68
+ });
69
+ }
70
+
71
+ async clear() {
72
+ this.table.clear();
73
+ }
74
+ }
75
+
76
+ export const SessionGroupModel = new _SessionGroupModel();
@@ -50,6 +50,7 @@ export const DB_SessionSchema = z.object({
50
50
  config: AgentSchema,
51
51
  group: z.string().default('default'),
52
52
  meta: LobeMetaDataSchema,
53
+ pinned: z.number().int().min(0).max(1).optional(),
53
54
  type: z.enum(['agent', 'group']).default('agent'),
54
55
  });
55
56
 
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+
3
+ export const DB_SessionGroupSchema = z.object({
4
+ name: z.string(),
5
+ sort: z.number().optional(),
6
+ });
7
+
8
+ export type DB_SessionGroup = z.infer<typeof DB_SessionGroupSchema>;
@@ -22,7 +22,8 @@ const Clear = memo(() => {
22
22
 
23
23
  return (
24
24
  <Popconfirm
25
- okButtonProps={{ danger: true }}
25
+ arrow={false}
26
+ okButtonProps={{ danger: true, type: 'primary' }}
26
27
  onConfirm={() => {
27
28
  resetConversation();
28
29
  }}
@@ -19,11 +19,11 @@ export const AssistantActionsBar: RenderAction = memo(({ id, onActionClick, erro
19
19
  dropdownMenu={[
20
20
  edit,
21
21
  copy,
22
- regenerate,
23
22
  divider,
24
23
  tts,
25
24
  translate,
26
25
  divider,
26
+ regenerate,
27
27
  delAndRegenerate,
28
28
  del,
29
29
  ]}
@@ -5,10 +5,10 @@ import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
5
5
  import { RenderAction } from '../types';
6
6
 
7
7
  export const FunctionActionsBar: RenderAction = memo(({ onActionClick }) => {
8
- const { regenerate, divider, delAndRegenerate, del } = useChatListActionsBar();
8
+ const { regenerate, delAndRegenerate, del } = useChatListActionsBar();
9
9
  return (
10
10
  <ActionIconGroup
11
- dropdownMenu={[regenerate, divider, delAndRegenerate, del]}
11
+ dropdownMenu={[regenerate, delAndRegenerate, del]}
12
12
  items={[regenerate]}
13
13
  onActionClick={onActionClick}
14
14
  type="ghost"
@@ -11,7 +11,7 @@ export const UserActionsBar: RenderAction = memo(({ onActionClick }) => {
11
11
 
12
12
  return (
13
13
  <ActionIconGroup
14
- dropdownMenu={[edit, copy, regenerate, divider, tts, translate, divider, del]}
14
+ dropdownMenu={[edit, copy, divider, tts, translate, divider, regenerate, del]}
15
15
  items={[regenerate, edit]}
16
16
  onActionClick={onActionClick}
17
17
  type="ghost"
@@ -1,5 +1,5 @@
1
1
  import { ActionIconGroupItems } from '@lobehub/ui/es/ActionIconGroup';
2
- import { Copy, Edit, RotateCw, Trash } from 'lucide-react';
2
+ import { Copy, Edit, ListRestart, RotateCcw, Trash } from 'lucide-react';
3
3
  import { useMemo } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
@@ -29,7 +29,7 @@ export const useChatListActionsBar = (): ChatListActionsBar => {
29
29
  label: t('delete', { defaultValue: 'Delete' }),
30
30
  },
31
31
  delAndRegenerate: {
32
- icon: RotateCw,
32
+ icon: ListRestart,
33
33
  key: 'delAndRegenerate',
34
34
  label: t('messageAction.delAndRegenerate', {
35
35
  defaultValue: 'Delete and regenerate',
@@ -45,7 +45,7 @@ export const useChatListActionsBar = (): ChatListActionsBar => {
45
45
  label: t('edit', { defaultValue: 'Edit' }),
46
46
  },
47
47
  regenerate: {
48
- icon: RotateCw,
48
+ icon: RotateCcw,
49
49
  key: 'regenerate',
50
50
  label: t('regenerate', { defaultValue: 'Regenerate' }),
51
51
  },
@@ -41,10 +41,13 @@ const DevModal = memo<DevModalProps>(
41
41
  <Flexbox flex={1} gap={12} horizontal justify={'flex-end'}>
42
42
  {isEditMode ? (
43
43
  <Popconfirm
44
+ arrow={false}
45
+ cancelText={t('cancel', { ns: 'common' })}
44
46
  okButtonProps={{
45
47
  danger: true,
46
48
  type: 'primary',
47
49
  }}
50
+ okText={t('ok', { ns: 'common' })}
48
51
  onConfirm={() => {
49
52
  onDelete?.();
50
53
  message.success(t('dev.deleteSuccess'));
@@ -69,7 +69,9 @@ const Actions = memo<ActionsProps>(({ identifier, type }) => {
69
69
  label: (
70
70
  <Popconfirm
71
71
  arrow={false}
72
+ cancelText={t('cancel', { ns: 'common' })}
72
73
  okButtonProps={{ danger: true }}
74
+ okText={t('ok', { ns: 'common' })}
73
75
  onConfirm={() => {
74
76
  unInstallPlugin(identifier);
75
77
  }}
@@ -7,6 +7,7 @@ export default {
7
7
  confirmClearCurrentMessages: '即将清空当前会话消息,清空后将无法找回,请确认你的操作',
8
8
  confirmRemoveSessionItemAlert: '即将删除该助手,删除后该将无法找回,请确认你的操作',
9
9
  defaultAgent: '自定义助手',
10
+ defaultList: '默认列表',
10
11
  defaultSession: '自定义助手',
11
12
  duplicateTitle: '{{title}} 副本',
12
13
  historyRange: '历史范围',
@@ -36,7 +37,18 @@ export default {
36
37
  roleAndArchive: '角色与记录',
37
38
  searchAgentPlaceholder: '搜索助手和对话...',
38
39
  sendPlaceholder: '输入聊天内容...',
39
- sessionList: '助手列表',
40
+ sessionGroup: {
41
+ config: '分组管理',
42
+ confirmRemoveGroupAlert: '即将删除该分组,删除后该分组的助手将移动到默认列表,请确认你的操作',
43
+ createGroup: '添加新分组',
44
+ createSuccess: '创建成功',
45
+ inputPlaceholder: '请输入分组名称...',
46
+ moveGroup: '移动到分组',
47
+ newGroup: '新分组',
48
+ rename: '重命名分组',
49
+ renameSuccess: '重命名成功',
50
+ tooLong: '分组名称长度需在 1-20 之内',
51
+ },
40
52
  shareModal: {
41
53
  download: '下载截图',
42
54
  imageType: '图片格式',
@@ -44,6 +44,7 @@ export default {
44
44
  added: '导入成功',
45
45
  errors: '导入出错',
46
46
  messages: '消息',
47
+ sessionGroups: '分组',
47
48
  sessions: '助手',
48
49
  skips: '重复跳过',
49
50
  topics: '话题',
@@ -0,0 +1,72 @@
1
+ {
2
+ "exportType": "sessions",
3
+ "state": {
4
+ "sessions": [
5
+ {
6
+ "config": {
7
+ "displayMode": "chat",
8
+ "historyCount": 1,
9
+ "model": "gpt-3.5-turbo",
10
+ "params": {
11
+ "frequency_penalty": 0,
12
+ "presence_penalty": 0,
13
+ "temperature": 0.6,
14
+ "top_p": 1,
15
+ "max_tokens": 2200
16
+ },
17
+ "plugins": [],
18
+ "systemRole": "你是一名 Postgresql 的数据库专家。用户是一名数据库小白,你需要使用简单直白的方式告诉用户如何使用 ostgresql 和它相应的 orm 工具 primsa",
19
+ "enableCompressThreshold": false,
20
+ "enableMaxTokens": false,
21
+ "inputTemplate": ""
22
+ },
23
+ "createdAt": 1698463356269,
24
+ "id": "06cc3e20-e870-4099-a619-c07a849d742d",
25
+ "meta": {
26
+ "avatar": "🐘",
27
+ "backgroundColor": "rgba(0,0,0,0)",
28
+ "title": "Postgresql 指南",
29
+ "description": "PostgreSQL 数据库和 Prisma ORM 工具的简单使用指南",
30
+ "tags": ["数据库", "PostgreSQL", "教程", "ORM", "Prisma"]
31
+ },
32
+ "type": "agent",
33
+ "updatedAt": 1698463356269,
34
+ "group": "pinned"
35
+ },
36
+ {
37
+ "config": {
38
+ "displayMode": "chat",
39
+ "historyCount": 1,
40
+ "model": "gpt-3.5-turbo",
41
+ "params": {
42
+ "frequency_penalty": 0,
43
+ "presence_penalty": 0,
44
+ "temperature": 0.6,
45
+ "top_p": 1,
46
+ "max_tokens": 2200
47
+ },
48
+ "plugins": [],
49
+ "systemRole": "你是一名 Postgresql 的数据库专家。用户是一名数据库小白,你需要使用简单直白的方式告诉用户如何使用 ostgresql 和它相应的 orm 工具 primsa",
50
+ "enableCompressThreshold": false,
51
+ "enableMaxTokens": false,
52
+ "inputTemplate": ""
53
+ },
54
+ "createdAt": 1698463356269,
55
+ "id": "06cc3e20-e870-4098-a619-c07a849d74dd",
56
+ "meta": {
57
+ "avatar": "🐘",
58
+ "backgroundColor": "rgba(0,0,0,0)",
59
+ "title": "Postgresql 指南",
60
+ "description": "PostgreSQL 数据库和 Prisma ORM 工具的简单使用指南",
61
+ "tags": ["数据库", "PostgreSQL", "教程", "ORM", "Prisma"]
62
+ },
63
+ "type": "agent",
64
+ "updatedAt": 1698463356269,
65
+ "group": "default"
66
+ }
67
+ ],
68
+ "topics": [],
69
+ "messages": []
70
+ },
71
+ "version": 2
72
+ }