@lobehub/chat 0.120.6 → 0.121.1
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.
- package/.github/ISSUE_TEMPLATE/2_feature_request_cn.yml +1 -1
- package/CHANGELOG.md +58 -0
- package/README.md +14 -14
- package/README.zh-CN.md +14 -14
- package/docs/Development/Feature-Development-Frontend.zh-CN.md +126 -0
- package/docs/Development/Feature-Development.zh-CN.md +664 -83
- package/docs/Development/{State-Management/State-Management-Intro.zh-CN.md → State-Management-Intro.zh-CN.md} +9 -9
- package/docs/Home.md +1 -0
- package/docs/_Sidebar.md +1 -0
- package/locales/ar/chat.json +13 -1
- package/locales/ar/common.json +1 -0
- package/locales/de-DE/chat.json +13 -1
- package/locales/de-DE/common.json +1 -0
- package/locales/en-US/chat.json +13 -1
- package/locales/en-US/common.json +1 -0
- package/locales/es-ES/chat.json +13 -1
- package/locales/es-ES/common.json +1 -0
- package/locales/fr-FR/chat.json +13 -1
- package/locales/fr-FR/common.json +1 -0
- package/locales/it-IT/chat.json +13 -1
- package/locales/it-IT/common.json +1 -0
- package/locales/ja-JP/chat.json +13 -1
- package/locales/ja-JP/common.json +1 -0
- package/locales/ko-KR/chat.json +13 -1
- package/locales/ko-KR/common.json +1 -0
- package/locales/nl-NL/chat.json +13 -1
- package/locales/nl-NL/common.json +1 -0
- package/locales/pl-PL/chat.json +13 -1
- package/locales/pl-PL/common.json +1 -0
- package/locales/pt-BR/chat.json +13 -1
- package/locales/pt-BR/common.json +1 -0
- package/locales/ru-RU/chat.json +13 -1
- package/locales/ru-RU/common.json +1 -0
- package/locales/tr-TR/chat.json +13 -1
- package/locales/tr-TR/common.json +1 -0
- package/locales/vi-VN/chat.json +13 -1
- package/locales/vi-VN/common.json +1 -0
- package/locales/zh-CN/chat.json +13 -1
- package/locales/zh-CN/common.json +1 -0
- package/locales/zh-TW/chat.json +13 -1
- package/locales/zh-TW/common.json +1 -0
- package/package.json +4 -6
- package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +18 -5
- package/src/app/chat/(desktop)/features/ChatInput/index.tsx +1 -1
- package/src/app/chat/features/ChatHeader/ShareButton/index.tsx +0 -1
- package/src/app/chat/features/Migration/Failed.tsx +2 -0
- package/src/app/chat/features/SessionListContent/CollapseGroup/Actions.tsx +115 -0
- package/src/app/chat/features/SessionListContent/CollapseGroup/index.tsx +6 -0
- package/src/app/chat/features/SessionListContent/DefaultMode.tsx +63 -23
- package/src/app/chat/features/SessionListContent/List/AddButton.tsx +3 -3
- package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +89 -35
- package/src/app/chat/features/SessionListContent/List/Item/index.tsx +35 -15
- package/src/app/chat/features/SessionListContent/List/index.tsx +4 -2
- package/src/app/chat/features/SessionListContent/Modals/ConfigGroupModal/GroupItem.tsx +84 -0
- package/src/app/chat/features/SessionListContent/Modals/ConfigGroupModal/index.tsx +79 -0
- package/src/app/chat/features/SessionListContent/Modals/CreateGroupModal.tsx +63 -0
- package/src/app/chat/features/SessionListContent/Modals/RenameGroupModal.tsx +50 -0
- package/src/app/chat/features/SessionListContent/SearchMode.tsx +1 -1
- package/src/app/settings/common/Common.tsx +11 -2
- package/src/app/settings/llm/LLM/index.tsx +1 -0
- package/src/components/FullscreenLoading/index.tsx +1 -1
- package/src/database/core/__tests__/db-upgrade.test.ts +42 -0
- package/src/database/core/db.ts +26 -4
- package/src/database/core/schemas.ts +14 -1
- package/src/database/core/types/db.ts +2 -0
- package/src/database/models/__tests__/message.test.ts +42 -0
- package/src/database/models/__tests__/plugin.test.ts +77 -0
- package/src/database/models/__tests__/session.test.ts +202 -63
- package/src/database/models/__tests__/sessionGroup.test.ts +195 -0
- package/src/database/models/__tests__/topic.test.ts +76 -1
- package/src/database/models/message.ts +7 -8
- package/src/database/models/session.ts +103 -7
- package/src/database/models/sessionGroup.ts +76 -0
- package/src/database/schemas/session.ts +1 -0
- package/src/database/schemas/sessionGroup.ts +8 -0
- package/src/features/ChatInput/ActionBar/Clear.tsx +2 -1
- package/src/features/Conversation/Actions/Assistant.tsx +1 -1
- package/src/features/Conversation/Actions/Function.tsx +2 -2
- package/src/features/Conversation/Actions/User.tsx +1 -1
- package/src/features/Conversation/Error/ApiKeyForm.tsx +1 -0
- package/src/features/Conversation/Error/InvalidAccess.tsx +1 -0
- package/src/features/Conversation/Plugins/Inspector/index.tsx +1 -1
- package/src/features/Conversation/Plugins/Render/DefaultType/SystemJsRender/index.tsx +7 -2
- package/src/features/Conversation/hooks/useChatListActionsBar.tsx +3 -3
- package/src/features/PluginDevModal/index.tsx +3 -0
- package/src/features/PluginSettings/PluginSettingRender.tsx +1 -0
- package/src/features/PluginStore/PluginItem/Action.tsx +2 -0
- package/src/locales/default/chat.ts +13 -1
- package/src/locales/default/common.ts +1 -0
- package/src/migrations/FromV2ToV3/fixtures/input-v2-session.json +72 -0
- package/src/migrations/FromV2ToV3/fixtures/output-v3-from-v1.json +203 -0
- package/src/migrations/FromV2ToV3/fixtures/output-v3.json +74 -0
- package/src/migrations/FromV2ToV3/index.ts +30 -0
- package/src/migrations/FromV2ToV3/migrations.test.ts +42 -0
- package/src/migrations/FromV2ToV3/types/v3.ts +27 -0
- package/src/migrations/index.ts +7 -1
- package/src/services/__tests__/session.test.ts +198 -7
- package/src/services/config.ts +23 -7
- package/src/services/session.ts +67 -14
- package/src/store/global/slices/common/action.test.ts +130 -0
- package/src/store/global/slices/common/action.ts +14 -0
- package/src/store/global/slices/common/initialState.ts +6 -2
- package/src/store/global/slices/common/selectors.ts +1 -4
- package/src/store/global/slices/settings/__snapshots__/selectors.test.ts.snap +83 -0
- package/src/store/global/slices/settings/action.ts +2 -2
- package/src/store/global/slices/settings/selectors.test.ts +132 -182
- package/src/store/global/store.ts +7 -0
- package/src/store/session/initialState.ts +5 -30
- package/src/store/session/selectors.ts +2 -1
- package/src/store/session/slices/session/action.test.ts +56 -0
- package/src/store/session/slices/session/action.ts +13 -9
- package/src/store/session/slices/session/helpers.ts +2 -3
- package/src/store/session/slices/session/initialState.ts +31 -23
- package/src/store/session/slices/session/selectors/index.ts +0 -1
- package/src/store/session/slices/session/selectors/list.ts +11 -18
- package/src/store/session/slices/sessionGroup/action.ts +54 -0
- package/src/store/session/slices/sessionGroup/initialState.ts +5 -0
- package/src/store/session/slices/sessionGroup/selectors.ts +15 -0
- package/src/store/session/store.ts +9 -2
- package/src/styles/antdOverride.ts +0 -7
- package/src/types/exportConfig.ts +3 -1
- package/src/types/session.ts +27 -3
- package/src/store/session/slices/session/selectors/export.ts +0 -31
- /package/docs/Development/{Feature-Development.md → Feature-Development-Frontend.md} +0 -0
- /package/docs/Development/{State-Management/State-Management-Intro.md → State-Management-Intro.md} +0 -0
- /package/docs/Development/{State-Management/Selectors.md → State-Management-Selectors.md} +0 -0
- /package/docs/Development/{State-Management/Selectors.zh-CN.md → State-Management-Selectors.zh-CN.md} +0 -0
package/src/services/config.ts
CHANGED
|
@@ -4,10 +4,10 @@ import { topicService } from '@/services/topic';
|
|
|
4
4
|
import { useGlobalStore } from '@/store/global';
|
|
5
5
|
import { settingsSelectors } from '@/store/global/selectors';
|
|
6
6
|
import { useSessionStore } from '@/store/session';
|
|
7
|
-
import {
|
|
7
|
+
import { sessionSelectors } from '@/store/session/selectors';
|
|
8
8
|
import { ConfigFile } from '@/types/exportConfig';
|
|
9
9
|
import { ChatMessage } from '@/types/message';
|
|
10
|
-
import { LobeSessions } from '@/types/session';
|
|
10
|
+
import { LobeSessions, SessionGroupItem } from '@/types/session';
|
|
11
11
|
import { GlobalSettings } from '@/types/settings';
|
|
12
12
|
import { ChatTopic } from '@/types/topic';
|
|
13
13
|
import { createConfigFile, exportConfigFile } from '@/utils/config';
|
|
@@ -19,6 +19,7 @@ export interface ImportResult {
|
|
|
19
19
|
}
|
|
20
20
|
export interface ImportResults {
|
|
21
21
|
messages?: ImportResult;
|
|
22
|
+
sessionGroups?: ImportResult;
|
|
22
23
|
sessions: ImportResult;
|
|
23
24
|
topics?: ImportResult;
|
|
24
25
|
}
|
|
@@ -40,6 +41,9 @@ class ConfigService {
|
|
|
40
41
|
importTopics = async (topics: ChatTopic[]) => {
|
|
41
42
|
return topicService.batchCreateTopics(topics);
|
|
42
43
|
};
|
|
44
|
+
importSessionGroups = async (sessionGroups: SessionGroupItem[]) => {
|
|
45
|
+
return sessionService.batchCreateSessionGroups(sessionGroups);
|
|
46
|
+
};
|
|
43
47
|
|
|
44
48
|
importConfigState = async (config: ConfigFile): Promise<ImportResults | undefined> => {
|
|
45
49
|
switch (config.exportType) {
|
|
@@ -50,8 +54,11 @@ class ConfigService {
|
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
case 'agents': {
|
|
57
|
+
const sessionGroups = await this.importSessionGroups(config.state.sessionGroups);
|
|
58
|
+
|
|
53
59
|
const data = await this.importSessions(config.state.sessions);
|
|
54
60
|
return {
|
|
61
|
+
sessionGroups: this.mapImportResult(sessionGroups),
|
|
55
62
|
sessions: this.mapImportResult(data),
|
|
56
63
|
};
|
|
57
64
|
}
|
|
@@ -59,6 +66,8 @@ class ConfigService {
|
|
|
59
66
|
case 'all': {
|
|
60
67
|
await this.importSettings(config.state.settings);
|
|
61
68
|
|
|
69
|
+
const sessionGroups = await this.importSessionGroups(config.state.sessionGroups);
|
|
70
|
+
|
|
62
71
|
const [sessions, messages, topics] = await Promise.all([
|
|
63
72
|
this.importSessions(config.state.sessions),
|
|
64
73
|
this.importMessages(config.state.messages),
|
|
@@ -67,12 +76,15 @@ class ConfigService {
|
|
|
67
76
|
|
|
68
77
|
return {
|
|
69
78
|
messages: this.mapImportResult(messages),
|
|
79
|
+
sessionGroups: this.mapImportResult(sessionGroups),
|
|
70
80
|
sessions: this.mapImportResult(sessions),
|
|
71
81
|
topics: this.mapImportResult(topics),
|
|
72
82
|
};
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
case 'sessions': {
|
|
86
|
+
const sessionGroups = await this.importSessionGroups(config.state.sessionGroups);
|
|
87
|
+
|
|
76
88
|
const [sessions, messages, topics] = await Promise.all([
|
|
77
89
|
this.importSessions(config.state.sessions),
|
|
78
90
|
this.importMessages(config.state.messages),
|
|
@@ -81,6 +93,7 @@ class ConfigService {
|
|
|
81
93
|
|
|
82
94
|
return {
|
|
83
95
|
messages: this.mapImportResult(messages),
|
|
96
|
+
sessionGroups: this.mapImportResult(sessionGroups),
|
|
84
97
|
sessions: this.mapImportResult(sessions),
|
|
85
98
|
topics: this.mapImportResult(topics),
|
|
86
99
|
};
|
|
@@ -93,8 +106,9 @@ class ConfigService {
|
|
|
93
106
|
*/
|
|
94
107
|
exportAgents = async () => {
|
|
95
108
|
const agents = await sessionService.getAllAgents();
|
|
109
|
+
const sessionGroups = await sessionService.getSessionGroups();
|
|
96
110
|
|
|
97
|
-
const config = createConfigFile('agents', { sessions: agents });
|
|
111
|
+
const config = createConfigFile('agents', { sessionGroups, sessions: agents });
|
|
98
112
|
|
|
99
113
|
exportConfigFile(config, 'agents');
|
|
100
114
|
};
|
|
@@ -104,10 +118,11 @@ class ConfigService {
|
|
|
104
118
|
*/
|
|
105
119
|
exportSessions = async () => {
|
|
106
120
|
const sessions = await sessionService.getSessions();
|
|
121
|
+
const sessionGroups = await sessionService.getSessionGroups();
|
|
107
122
|
const messages = await messageService.getAllMessages();
|
|
108
123
|
const topics = await topicService.getAllTopics();
|
|
109
124
|
|
|
110
|
-
const config = createConfigFile('sessions', { messages, sessions, topics });
|
|
125
|
+
const config = createConfigFile('sessions', { messages, sessionGroups, sessions, topics });
|
|
111
126
|
|
|
112
127
|
exportConfigFile(config, 'sessions');
|
|
113
128
|
};
|
|
@@ -153,7 +168,7 @@ class ConfigService {
|
|
|
153
168
|
const agent = this.getAgent(id);
|
|
154
169
|
if (!agent) return;
|
|
155
170
|
|
|
156
|
-
const config = createConfigFile('agents', { sessions: [agent] });
|
|
171
|
+
const config = createConfigFile('agents', { sessionGroups: [], sessions: [agent] });
|
|
157
172
|
|
|
158
173
|
exportConfigFile(config, agent.meta?.title || 'agent');
|
|
159
174
|
};
|
|
@@ -174,11 +189,12 @@ class ConfigService {
|
|
|
174
189
|
*/
|
|
175
190
|
exportAll = async () => {
|
|
176
191
|
const sessions = await sessionService.getSessions();
|
|
192
|
+
const sessionGroups = await sessionService.getSessionGroups();
|
|
177
193
|
const messages = await messageService.getAllMessages();
|
|
178
194
|
const topics = await topicService.getAllTopics();
|
|
179
195
|
const settings = this.getSettings();
|
|
180
196
|
|
|
181
|
-
const config = createConfigFile('all', { messages, sessions, settings, topics });
|
|
197
|
+
const config = createConfigFile('all', { messages, sessionGroups, sessions, settings, topics });
|
|
182
198
|
|
|
183
199
|
exportConfigFile(config, 'config');
|
|
184
200
|
};
|
|
@@ -189,7 +205,7 @@ class ConfigService {
|
|
|
189
205
|
sessionSelectors.getSessionById(id)(useSessionStore.getState());
|
|
190
206
|
|
|
191
207
|
private getAgent = (id: string) =>
|
|
192
|
-
|
|
208
|
+
sessionSelectors.getSessionById(id)(useSessionStore.getState());
|
|
193
209
|
|
|
194
210
|
private mapImportResult = (input: {
|
|
195
211
|
added: number;
|
package/src/services/session.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { DeepPartial } from 'utility-types';
|
|
2
2
|
|
|
3
3
|
import { SessionModel } from '@/database/models/session';
|
|
4
|
+
import { SessionGroupModel } from '@/database/models/sessionGroup';
|
|
4
5
|
import { LobeAgentConfig } from '@/types/agent';
|
|
5
6
|
import { MetaData } from '@/types/meta';
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
ChatSessionList,
|
|
9
|
+
LobeAgentSession,
|
|
10
|
+
LobeSessionType,
|
|
11
|
+
LobeSessions,
|
|
12
|
+
SessionGroupId,
|
|
13
|
+
SessionGroupItem,
|
|
14
|
+
SessionGroups,
|
|
15
|
+
} from '@/types/session';
|
|
7
16
|
|
|
8
17
|
class SessionService {
|
|
9
18
|
async createNewSession(
|
|
@@ -21,26 +30,40 @@ class SessionService {
|
|
|
21
30
|
return SessionModel.batchCreate(importSessions);
|
|
22
31
|
}
|
|
23
32
|
|
|
33
|
+
async duplicateSession(id: string, newTitle: string): Promise<string | undefined> {
|
|
34
|
+
const res = await SessionModel.duplicate(id, newTitle);
|
|
35
|
+
|
|
36
|
+
if (res) return res?.id;
|
|
37
|
+
}
|
|
38
|
+
|
|
24
39
|
async getSessions(): Promise<LobeSessions> {
|
|
25
40
|
return SessionModel.query();
|
|
26
41
|
}
|
|
27
42
|
|
|
43
|
+
async getSessionsWithGroup(): Promise<ChatSessionList> {
|
|
44
|
+
return SessionModel.queryWithGroups();
|
|
45
|
+
}
|
|
46
|
+
|
|
28
47
|
async getAllAgents(): Promise<LobeSessions> {
|
|
29
48
|
// TODO: add a filter to get only agents
|
|
30
49
|
return await SessionModel.query();
|
|
31
50
|
}
|
|
32
51
|
|
|
33
|
-
async
|
|
34
|
-
|
|
52
|
+
async hasSessions() {
|
|
53
|
+
const isEmpty = await SessionModel.isEmpty();
|
|
54
|
+
return !isEmpty;
|
|
35
55
|
}
|
|
36
56
|
|
|
37
|
-
async
|
|
38
|
-
return SessionModel.
|
|
57
|
+
async searchSessions(keyword: string) {
|
|
58
|
+
return SessionModel.queryByKeyword(keyword);
|
|
39
59
|
}
|
|
40
60
|
|
|
41
|
-
async
|
|
61
|
+
async updateSessionGroupId(id: string, group: SessionGroupId) {
|
|
42
62
|
return SessionModel.update(id, { group });
|
|
43
63
|
}
|
|
64
|
+
async updateSessionPinned(id: string, pinned: boolean) {
|
|
65
|
+
return SessionModel.updatePinned(id, pinned);
|
|
66
|
+
}
|
|
44
67
|
|
|
45
68
|
async updateSessionMeta(activeId: string, meta: Partial<MetaData>) {
|
|
46
69
|
return SessionModel.update(activeId, { meta });
|
|
@@ -50,19 +73,49 @@ class SessionService {
|
|
|
50
73
|
return SessionModel.updateConfig(activeId, config);
|
|
51
74
|
}
|
|
52
75
|
|
|
53
|
-
async
|
|
54
|
-
|
|
55
|
-
return !isEmpty;
|
|
76
|
+
async removeSession(id: string) {
|
|
77
|
+
return SessionModel.delete(id);
|
|
56
78
|
}
|
|
57
79
|
|
|
58
|
-
async
|
|
59
|
-
return SessionModel.
|
|
80
|
+
async removeAllSessions() {
|
|
81
|
+
return SessionModel.clearTable();
|
|
60
82
|
}
|
|
61
83
|
|
|
62
|
-
|
|
63
|
-
|
|
84
|
+
// ************************************** //
|
|
85
|
+
// *********** SessionGroup *********** //
|
|
86
|
+
// ************************************** //
|
|
64
87
|
|
|
65
|
-
|
|
88
|
+
async createSessionGroup(name: string, sort?: number) {
|
|
89
|
+
const item = await SessionGroupModel.create(name, sort);
|
|
90
|
+
if (!item) {
|
|
91
|
+
throw new Error('session group create Error');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return item.id;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async batchCreateSessionGroups(groups: SessionGroups) {
|
|
98
|
+
return SessionGroupModel.batchCreate(groups);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async removeSessionGroup(id: string, removeChildren?: boolean) {
|
|
102
|
+
return await SessionGroupModel.delete(id, removeChildren);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async updateSessionGroup(id: string, data: Partial<SessionGroupItem>) {
|
|
106
|
+
return SessionGroupModel.update(id, data);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async updateSessionGroupOrder(sortMap: { id: string; sort: number }[]) {
|
|
110
|
+
return SessionGroupModel.updateOrder(sortMap);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async getSessionGroups(): Promise<SessionGroupItem[]> {
|
|
114
|
+
return SessionGroupModel.query();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async clearSessionGroups() {
|
|
118
|
+
return SessionGroupModel.clear();
|
|
66
119
|
}
|
|
67
120
|
}
|
|
68
121
|
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react';
|
|
2
|
+
import { router } from 'next/client';
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import { globalService } from '@/services/global';
|
|
6
|
+
import { useGlobalStore } from '@/store/global';
|
|
7
|
+
import { type Guide, SidebarTabKey } from '@/store/global/slices/common/initialState';
|
|
8
|
+
|
|
9
|
+
// Mock globalService
|
|
10
|
+
vi.mock('@/services/global', () => ({
|
|
11
|
+
globalService: {
|
|
12
|
+
getLatestVersion: vi.fn(),
|
|
13
|
+
getGlobalConfig: vi.fn(),
|
|
14
|
+
},
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vi.clearAllMocks();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
vi.restoreAllMocks();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('createCommonSlice', () => {
|
|
26
|
+
describe('switchBackToChat', () => {
|
|
27
|
+
it('should switch back to chat', () => {
|
|
28
|
+
const { result } = renderHook(() => useGlobalStore());
|
|
29
|
+
const sessionId = 'session-id';
|
|
30
|
+
const router = { push: vi.fn() } as any;
|
|
31
|
+
|
|
32
|
+
act(() => {
|
|
33
|
+
useGlobalStore.setState({ router });
|
|
34
|
+
result.current.switchBackToChat(sessionId);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(router.push).toHaveBeenCalledWith('/chat?session=session-id');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('switchSideBar', () => {
|
|
42
|
+
it('should switch sidebar', () => {
|
|
43
|
+
const { result } = renderHook(() => useGlobalStore());
|
|
44
|
+
|
|
45
|
+
act(() => {
|
|
46
|
+
result.current.switchSideBar(SidebarTabKey.Market);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(result.current.sidebarKey).toEqual(SidebarTabKey.Market);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('toggleChatSideBar', () => {
|
|
54
|
+
it('should toggle chat sidebar', () => {
|
|
55
|
+
const { result } = renderHook(() => useGlobalStore());
|
|
56
|
+
|
|
57
|
+
act(() => {
|
|
58
|
+
useGlobalStore.getState().updatePreference({ showChatSideBar: false });
|
|
59
|
+
result.current.toggleChatSideBar();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(result.current.preference.showChatSideBar).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Add more tests for other actions in createCommonSlice
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('toggleExpandSessionGroup', () => {
|
|
69
|
+
it('should toggle expand session group', () => {
|
|
70
|
+
const { result } = renderHook(() => useGlobalStore());
|
|
71
|
+
const groupId = 'group-id';
|
|
72
|
+
|
|
73
|
+
act(() => {
|
|
74
|
+
result.current.toggleExpandSessionGroup(groupId, true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(result.current.preference.expandSessionGroupKeys).toContain(groupId);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('toggleMobileTopic', () => {
|
|
82
|
+
it('should toggle mobile topic', () => {
|
|
83
|
+
const { result } = renderHook(() => useGlobalStore());
|
|
84
|
+
|
|
85
|
+
act(() => {
|
|
86
|
+
result.current.toggleMobileTopic();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(result.current.preference.mobileShowTopic).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('toggleSystemRole', () => {
|
|
94
|
+
it('should toggle system role', () => {
|
|
95
|
+
const { result } = renderHook(() => useGlobalStore());
|
|
96
|
+
|
|
97
|
+
act(() => {
|
|
98
|
+
result.current.toggleSystemRole(true);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(result.current.preference.showSystemRole).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('updateGuideState', () => {
|
|
106
|
+
it('should update guide state', () => {
|
|
107
|
+
const { result } = renderHook(() => useGlobalStore());
|
|
108
|
+
const guide: Guide = { topic: true };
|
|
109
|
+
|
|
110
|
+
act(() => {
|
|
111
|
+
result.current.updateGuideState(guide);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
expect(result.current.preference.guide).toEqual(guide);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('updatePreference', () => {
|
|
119
|
+
it('should update preference', () => {
|
|
120
|
+
const { result } = renderHook(() => useGlobalStore());
|
|
121
|
+
const preference = { inputHeight: 200 };
|
|
122
|
+
|
|
123
|
+
act(() => {
|
|
124
|
+
result.current.updatePreference(preference);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
expect(result.current.preference.inputHeight).toEqual(200);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -27,6 +27,7 @@ export interface CommonAction {
|
|
|
27
27
|
*/
|
|
28
28
|
switchSideBar: (key: SidebarTabKey) => void;
|
|
29
29
|
toggleChatSideBar: (visible?: boolean) => void;
|
|
30
|
+
toggleExpandSessionGroup: (id: string, expand: boolean) => void;
|
|
30
31
|
toggleMobileTopic: (visible?: boolean) => void;
|
|
31
32
|
toggleSystemRole: (visible?: boolean) => void;
|
|
32
33
|
updateGuideState: (guide: Partial<Guide>) => void;
|
|
@@ -53,6 +54,19 @@ export const createCommonSlice: StateCreator<
|
|
|
53
54
|
|
|
54
55
|
get().updatePreference({ showChatSideBar }, n('toggleAgentPanel', newValue) as string);
|
|
55
56
|
},
|
|
57
|
+
toggleExpandSessionGroup: (id, expand) => {
|
|
58
|
+
const { preference } = get();
|
|
59
|
+
const nextExpandSessionGroup = produce(preference.expandSessionGroupKeys, (draft) => {
|
|
60
|
+
if (expand) {
|
|
61
|
+
if (draft.includes(id)) return;
|
|
62
|
+
draft.push(id);
|
|
63
|
+
} else {
|
|
64
|
+
const index = draft.indexOf(id);
|
|
65
|
+
if (index !== -1) draft.splice(index, 1);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
get().updatePreference({ expandSessionGroupKeys: nextExpandSessionGroup });
|
|
69
|
+
},
|
|
56
70
|
toggleMobileTopic: (newValue) => {
|
|
57
71
|
const mobileShowTopic =
|
|
58
72
|
typeof newValue === 'boolean' ? newValue : !get().preference.mobileShowTopic;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
|
|
2
2
|
|
|
3
|
+
import { SessionDefaultGroup, SessionGroupId } from '@/types/session';
|
|
4
|
+
|
|
3
5
|
export enum SidebarTabKey {
|
|
4
6
|
Chat = 'chat',
|
|
5
7
|
Market = 'market',
|
|
@@ -19,11 +21,13 @@ export interface Guide {
|
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
export interface GlobalPreference {
|
|
24
|
+
// which sessionGroup should expand
|
|
25
|
+
expandSessionGroupKeys: SessionGroupId[];
|
|
22
26
|
guide?: Guide;
|
|
23
27
|
inputHeight: number;
|
|
24
28
|
mobileShowTopic?: boolean;
|
|
25
|
-
sessionGroupKeys: string[];
|
|
26
29
|
sessionsWidth: number;
|
|
30
|
+
|
|
27
31
|
showChatSideBar?: boolean;
|
|
28
32
|
showSessionPanel?: boolean;
|
|
29
33
|
showSystemRole?: boolean;
|
|
@@ -50,10 +54,10 @@ export interface GlobalCommonState {
|
|
|
50
54
|
export const initialCommonState: GlobalCommonState = {
|
|
51
55
|
isMobile: false,
|
|
52
56
|
preference: {
|
|
57
|
+
expandSessionGroupKeys: [SessionDefaultGroup.Pinned, SessionDefaultGroup.Default],
|
|
53
58
|
guide: {},
|
|
54
59
|
inputHeight: 200,
|
|
55
60
|
mobileShowTopic: false,
|
|
56
|
-
sessionGroupKeys: ['pinned', 'sessionList'],
|
|
57
61
|
sessionsWidth: 320,
|
|
58
62
|
showChatSideBar: true,
|
|
59
63
|
showSessionPanel: true,
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { GlobalStore } from '@/store/global';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const sessionGroupKeys = (s: GlobalStore): string[] =>
|
|
6
|
-
s.preference.sessionGroupKeys || initialState.preference.sessionGroupKeys;
|
|
3
|
+
const sessionGroupKeys = (s: GlobalStore): string[] => s.preference.expandSessionGroupKeys || [];
|
|
7
4
|
|
|
8
5
|
const useCmdEnterToSend = (s: GlobalStore): boolean => s.preference.useCmdEnterToSend || false;
|
|
9
6
|
|
|
@@ -161,3 +161,86 @@ exports[`settingsSelectors > currentSettings > should merge DEFAULT_SETTINGS and
|
|
|
161
161
|
},
|
|
162
162
|
}
|
|
163
163
|
`;
|
|
164
|
+
|
|
165
|
+
exports[`settingsSelectors > currentTTS > should merge DEFAULT_TTS_CONFIG and s.settings.tts correctly 1`] = `
|
|
166
|
+
{
|
|
167
|
+
"openAI": {
|
|
168
|
+
"sttModel": "whisper-2",
|
|
169
|
+
"ttsModel": "tts-1",
|
|
170
|
+
},
|
|
171
|
+
"sttAutoStop": false,
|
|
172
|
+
"sttServer": "openai",
|
|
173
|
+
}
|
|
174
|
+
`;
|
|
175
|
+
|
|
176
|
+
exports[`settingsSelectors > dalleConfig > should return the dalle configuration 1`] = `
|
|
177
|
+
{
|
|
178
|
+
"apiKey": "dalle-api-key",
|
|
179
|
+
"autoGenerate": true,
|
|
180
|
+
}
|
|
181
|
+
`;
|
|
182
|
+
|
|
183
|
+
exports[`settingsSelectors > defaultAgent > should merge DEFAULT_AGENT and s.settings.defaultAgent correctly 1`] = `
|
|
184
|
+
{
|
|
185
|
+
"config": {
|
|
186
|
+
"autoCreateTopicThreshold": 2,
|
|
187
|
+
"displayMode": "chat",
|
|
188
|
+
"enableAutoCreateTopic": true,
|
|
189
|
+
"historyCount": 1,
|
|
190
|
+
"model": "gpt-3.5-turbo",
|
|
191
|
+
"params": {
|
|
192
|
+
"frequency_penalty": 0,
|
|
193
|
+
"presence_penalty": 0,
|
|
194
|
+
"temperature": 0.6,
|
|
195
|
+
"top_p": 1,
|
|
196
|
+
},
|
|
197
|
+
"plugins": [],
|
|
198
|
+
"systemRole": "user",
|
|
199
|
+
"tts": {
|
|
200
|
+
"showAllLocaleVoice": false,
|
|
201
|
+
"sttLocale": "auto",
|
|
202
|
+
"ttsService": "openai",
|
|
203
|
+
"voice": {
|
|
204
|
+
"openai": "alloy",
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
"meta": {
|
|
209
|
+
"avatar": "agent-avatar.jpg",
|
|
210
|
+
"description": "Test agent",
|
|
211
|
+
},
|
|
212
|
+
}
|
|
213
|
+
`;
|
|
214
|
+
|
|
215
|
+
exports[`settingsSelectors > defaultAgentConfig > should merge DEFAULT_AGENT_CONFIG and defaultAgent(s).config correctly 1`] = `
|
|
216
|
+
{
|
|
217
|
+
"autoCreateTopicThreshold": 2,
|
|
218
|
+
"displayMode": "chat",
|
|
219
|
+
"enableAutoCreateTopic": true,
|
|
220
|
+
"historyCount": 1,
|
|
221
|
+
"model": "gpt-3.5-turbo",
|
|
222
|
+
"params": {
|
|
223
|
+
"frequency_penalty": 0,
|
|
224
|
+
"presence_penalty": 0,
|
|
225
|
+
"temperature": 0.7,
|
|
226
|
+
"top_p": 1,
|
|
227
|
+
},
|
|
228
|
+
"plugins": [],
|
|
229
|
+
"systemRole": "user",
|
|
230
|
+
"tts": {
|
|
231
|
+
"showAllLocaleVoice": false,
|
|
232
|
+
"sttLocale": "auto",
|
|
233
|
+
"ttsService": "openai",
|
|
234
|
+
"voice": {
|
|
235
|
+
"openai": "alloy",
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
}
|
|
239
|
+
`;
|
|
240
|
+
|
|
241
|
+
exports[`settingsSelectors > defaultAgentMeta > should merge DEFAULT_AGENT_META and defaultAgent(s).meta correctly 1`] = `
|
|
242
|
+
{
|
|
243
|
+
"avatar": "agent-avatar.jpg",
|
|
244
|
+
"description": "Test agent",
|
|
245
|
+
}
|
|
246
|
+
`;
|
|
@@ -24,12 +24,12 @@ export interface SettingsAction {
|
|
|
24
24
|
*/
|
|
25
25
|
resetSettings: () => void;
|
|
26
26
|
setOpenAIConfig: (config: Partial<OpenAIConfig>) => void;
|
|
27
|
-
|
|
28
27
|
/**
|
|
29
28
|
* 设置部分配置设置
|
|
30
29
|
* @param settings - 部分配置设置
|
|
31
30
|
*/
|
|
32
31
|
setSettings: (settings: DeepPartial<GlobalSettings>) => void;
|
|
32
|
+
|
|
33
33
|
switchSettingTabs: (tab: SettingsTabs) => void;
|
|
34
34
|
/**
|
|
35
35
|
* 设置主题模式
|
|
@@ -68,7 +68,6 @@ export const createSettingsSlice: StateCreator<
|
|
|
68
68
|
setOpenAIConfig: (config) => {
|
|
69
69
|
get().setSettings({ languageModel: { openAI: config } });
|
|
70
70
|
},
|
|
71
|
-
|
|
72
71
|
setSettings: (settings) => {
|
|
73
72
|
const prevSetting = get().settings;
|
|
74
73
|
const nextSettings = merge(prevSetting, settings);
|
|
@@ -77,6 +76,7 @@ export const createSettingsSlice: StateCreator<
|
|
|
77
76
|
|
|
78
77
|
set({ settings: merge(prevSetting, settings) }, false, n('setSettings', settings));
|
|
79
78
|
},
|
|
79
|
+
|
|
80
80
|
switchSettingTabs: (tab) => {
|
|
81
81
|
set({ settingsTab: tab });
|
|
82
82
|
},
|