@lobehub/chat 0.120.6 → 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.
- package/.github/ISSUE_TEMPLATE/2_feature_request_cn.yml +1 -1
- package/CHANGELOG.md +25 -0
- package/README.md +9 -9
- package/README.zh-CN.md +9 -9
- 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 +5 -1
- 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/hooks/useChatListActionsBar.tsx +3 -3
- package/src/features/PluginDevModal/index.tsx +3 -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
|
@@ -153,187 +153,137 @@ describe('settingsSelectors', () => {
|
|
|
153
153
|
expect(result).toMatchSnapshot();
|
|
154
154
|
});
|
|
155
155
|
});
|
|
156
|
+
describe('defaultAgent', () => {
|
|
157
|
+
it('should merge DEFAULT_AGENT and s.settings.defaultAgent correctly', () => {
|
|
158
|
+
const s = {
|
|
159
|
+
settings: {
|
|
160
|
+
defaultAgent: {
|
|
161
|
+
config: {
|
|
162
|
+
systemRole: 'user',
|
|
163
|
+
model: LanguageModel.GPT3_5,
|
|
164
|
+
},
|
|
165
|
+
meta: {
|
|
166
|
+
avatar: 'agent-avatar.jpg',
|
|
167
|
+
description: 'Test agent',
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
} as unknown as GlobalStore;
|
|
172
|
+
|
|
173
|
+
const result = settingsSelectors.defaultAgent(s);
|
|
174
|
+
|
|
175
|
+
expect(result).toMatchSnapshot();
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('defaultAgentConfig', () => {
|
|
180
|
+
it('should merge DEFAULT_AGENT_CONFIG and defaultAgent(s).config correctly', () => {
|
|
181
|
+
const s = {
|
|
182
|
+
settings: {
|
|
183
|
+
defaultAgent: {
|
|
184
|
+
config: {
|
|
185
|
+
systemRole: 'user',
|
|
186
|
+
model: LanguageModel.GPT3_5,
|
|
187
|
+
params: {
|
|
188
|
+
temperature: 0.7,
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
} as unknown as GlobalStore;
|
|
194
|
+
|
|
195
|
+
const result = settingsSelectors.defaultAgentConfig(s);
|
|
196
|
+
|
|
197
|
+
expect(result).toMatchSnapshot();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe('defaultAgentMeta', () => {
|
|
202
|
+
it('should merge DEFAULT_AGENT_META and defaultAgent(s).meta correctly', () => {
|
|
203
|
+
const s = {
|
|
204
|
+
settings: {
|
|
205
|
+
defaultAgent: {
|
|
206
|
+
meta: {
|
|
207
|
+
avatar: 'agent-avatar.jpg',
|
|
208
|
+
description: 'Test agent',
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
} as unknown as GlobalStore;
|
|
213
|
+
|
|
214
|
+
const result = settingsSelectors.defaultAgentMeta(s);
|
|
215
|
+
|
|
216
|
+
expect(result).toMatchSnapshot();
|
|
217
|
+
});
|
|
218
|
+
});
|
|
156
219
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
// const s: GlobalStore = {
|
|
227
|
-
// settings: {
|
|
228
|
-
// OPENAI_API_KEY: 'openai-api-key',
|
|
229
|
-
// password: 'password123',
|
|
230
|
-
// avatar: 'avatar.jpg',
|
|
231
|
-
// fontSize: 14,
|
|
232
|
-
// language: 'en',
|
|
233
|
-
// neutralColor: 'white',
|
|
234
|
-
// primaryColor: 'blue',
|
|
235
|
-
// themeMode: 'light',
|
|
236
|
-
// defaultAgent: {
|
|
237
|
-
// config: {
|
|
238
|
-
// model: 'gpt-3.5-turbo',
|
|
239
|
-
// maxTokens: 100,
|
|
240
|
-
// },
|
|
241
|
-
// meta: {
|
|
242
|
-
// name: 'Default Agent',
|
|
243
|
-
// description: 'Default agent for testing',
|
|
244
|
-
// },
|
|
245
|
-
// },
|
|
246
|
-
// languageModel: {
|
|
247
|
-
// azureOpenAI: {
|
|
248
|
-
// AZURE_API_KEY: 'azure-api-key',
|
|
249
|
-
// apiVersion: 'v1',
|
|
250
|
-
// endpoint: 'https://azure-openai-endpoint.com',
|
|
251
|
-
// models: ['gpt-3.5-turbo'],
|
|
252
|
-
// },
|
|
253
|
-
// openAI: {
|
|
254
|
-
// OPENAI_API_KEY: 'openai-api-key',
|
|
255
|
-
// endpoint: 'https://openai-endpoint.com',
|
|
256
|
-
// models: ['gpt-3.5-turbo'],
|
|
257
|
-
// },
|
|
258
|
-
// },
|
|
259
|
-
// },
|
|
260
|
-
// };
|
|
261
|
-
//
|
|
262
|
-
// const result = settingsSelectors.exportSettings(s);
|
|
263
|
-
//
|
|
264
|
-
// const expected = {
|
|
265
|
-
// avatar: 'avatar.jpg',
|
|
266
|
-
// fontSize: 14,
|
|
267
|
-
// language: 'en',
|
|
268
|
-
// neutralColor: 'white',
|
|
269
|
-
// primaryColor: 'blue',
|
|
270
|
-
// themeMode: 'light',
|
|
271
|
-
// defaultAgent: {
|
|
272
|
-
// config: {
|
|
273
|
-
// model: 'gpt-3.5-turbo',
|
|
274
|
-
// maxTokens: 100,
|
|
275
|
-
// },
|
|
276
|
-
// meta: {
|
|
277
|
-
// name: 'Default Agent',
|
|
278
|
-
// description: 'Default agent for testing',
|
|
279
|
-
// },
|
|
280
|
-
// },
|
|
281
|
-
// languageModel: {
|
|
282
|
-
// azureOpenAI: {
|
|
283
|
-
// AZURE_API_KEY: 'azure-api-key',
|
|
284
|
-
// apiVersion: 'v1',
|
|
285
|
-
// endpoint: 'https://azure-openai-endpoint.com',
|
|
286
|
-
// models: ['gpt-3.5-turbo'],
|
|
287
|
-
// },
|
|
288
|
-
// openAI: {
|
|
289
|
-
// endpoint: 'https://openai-endpoint.com',
|
|
290
|
-
// models: ['gpt-3.5-turbo'],
|
|
291
|
-
// },
|
|
292
|
-
// },
|
|
293
|
-
// };
|
|
294
|
-
//
|
|
295
|
-
// expect(result).toEqual(expected);
|
|
296
|
-
// });
|
|
297
|
-
//
|
|
298
|
-
// it('should return the result as GlobalSettings type', () => {
|
|
299
|
-
// const s: GlobalStore = {
|
|
300
|
-
// settings: {
|
|
301
|
-
// avatar: 'avatar.jpg',
|
|
302
|
-
// fontSize: 14,
|
|
303
|
-
// language: 'en',
|
|
304
|
-
// neutralColor: 'white',
|
|
305
|
-
// password: 'password123',
|
|
306
|
-
// primaryColor: 'blue',
|
|
307
|
-
// themeMode: 'light',
|
|
308
|
-
// defaultAgent: {
|
|
309
|
-
// config: {
|
|
310
|
-
// model: 'gpt-3.5-turbo',
|
|
311
|
-
// maxTokens: 100,
|
|
312
|
-
// },
|
|
313
|
-
// meta: {
|
|
314
|
-
// name: 'Default Agent',
|
|
315
|
-
// description: 'Default agent for testing',
|
|
316
|
-
// },
|
|
317
|
-
// },
|
|
318
|
-
// languageModel: {
|
|
319
|
-
// azureOpenAI: {
|
|
320
|
-
// AZURE_API_KEY: 'azure-api-key',
|
|
321
|
-
// apiVersion: 'v1',
|
|
322
|
-
// endpoint: 'https://azure-openai-endpoint.com',
|
|
323
|
-
// models: ['gpt-3.5-turbo'],
|
|
324
|
-
// },
|
|
325
|
-
// openAI: {
|
|
326
|
-
// OPENAI_API_KEY: 'openai-api-key',
|
|
327
|
-
// endpoint: 'https://openai-endpoint.com',
|
|
328
|
-
// models: ['gpt-3.5-turbo'],
|
|
329
|
-
// },
|
|
330
|
-
// },
|
|
331
|
-
// },
|
|
332
|
-
// };
|
|
333
|
-
//
|
|
334
|
-
// const result = settingsSelectors.exportSettings(s);
|
|
335
|
-
//
|
|
336
|
-
// expect(result).toBeInstanceOf(GlobalSettings);
|
|
337
|
-
// });
|
|
338
|
-
// });
|
|
220
|
+
describe('currentTTS', () => {
|
|
221
|
+
it('should merge DEFAULT_TTS_CONFIG and s.settings.tts correctly', () => {
|
|
222
|
+
const s = {
|
|
223
|
+
settings: {
|
|
224
|
+
tts: {
|
|
225
|
+
sttAutoStop: false,
|
|
226
|
+
openAI: {
|
|
227
|
+
sttModel: 'whisper-2',
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
} as unknown as GlobalStore;
|
|
232
|
+
|
|
233
|
+
const result = settingsSelectors.currentTTS(s);
|
|
234
|
+
|
|
235
|
+
expect(result).toMatchSnapshot();
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe('currentLanguage', () => {
|
|
240
|
+
it('should return the correct language setting', () => {
|
|
241
|
+
const s = {
|
|
242
|
+
settings: {
|
|
243
|
+
language: 'fr',
|
|
244
|
+
},
|
|
245
|
+
} as unknown as GlobalStore;
|
|
246
|
+
|
|
247
|
+
const result = settingsSelectors.currentLanguage(s);
|
|
248
|
+
|
|
249
|
+
expect(result).toBe('fr');
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
describe('dalleConfig', () => {
|
|
254
|
+
it('should return the dalle configuration', () => {
|
|
255
|
+
const s = {
|
|
256
|
+
settings: {
|
|
257
|
+
tool: {
|
|
258
|
+
dalle: {
|
|
259
|
+
apiKey: 'dalle-api-key',
|
|
260
|
+
autoGenerate: true,
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
} as unknown as GlobalStore;
|
|
265
|
+
|
|
266
|
+
const result = settingsSelectors.dalleConfig(s);
|
|
267
|
+
|
|
268
|
+
expect(result).toMatchSnapshot();
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe('isDalleAutoGenerating', () => {
|
|
273
|
+
it('should return the autoGenerate flag from dalle configuration', () => {
|
|
274
|
+
const s = {
|
|
275
|
+
settings: {
|
|
276
|
+
tool: {
|
|
277
|
+
dalle: {
|
|
278
|
+
autoGenerate: true,
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
} as unknown as GlobalStore;
|
|
283
|
+
|
|
284
|
+
const result = settingsSelectors.isDalleAutoGenerating(s);
|
|
285
|
+
|
|
286
|
+
expect(result).toBe(true);
|
|
287
|
+
});
|
|
288
|
+
});
|
|
339
289
|
});
|
|
@@ -5,6 +5,7 @@ import { createWithEqualityFn } from 'zustand/traditional';
|
|
|
5
5
|
import { StateCreator } from 'zustand/vanilla';
|
|
6
6
|
|
|
7
7
|
import { DEFAULT_AGENT, DEFAULT_LLM_CONFIG } from '@/const/settings';
|
|
8
|
+
import { SessionDefaultGroup } from '@/types/session';
|
|
8
9
|
import { isDev } from '@/utils/env';
|
|
9
10
|
|
|
10
11
|
import { createHyperStorage } from '../middleware/createHyperStorage';
|
|
@@ -32,6 +33,12 @@ const persistOptions: PersistOptions<GlobalStore, GlobalPersist> = {
|
|
|
32
33
|
return {
|
|
33
34
|
...currentState,
|
|
34
35
|
...state,
|
|
36
|
+
preference: produce(state.preference, (draft) => {
|
|
37
|
+
if (!draft.expandSessionGroupKeys) {
|
|
38
|
+
draft.expandSessionGroupKeys = [SessionDefaultGroup.Pinned, SessionDefaultGroup.Default];
|
|
39
|
+
delete (draft as any).sessionGroupKeys;
|
|
40
|
+
}
|
|
41
|
+
}),
|
|
35
42
|
settings: produce(state.settings, (draft) => {
|
|
36
43
|
if (!draft.defaultAgent) {
|
|
37
44
|
draft.defaultAgent = DEFAULT_AGENT;
|
|
@@ -1,34 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SessionState, initialSessionState } from './slices/session/initialState';
|
|
2
|
+
import { SessionGroupState, initSessionGroupState } from './slices/sessionGroup/initialState';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export type SessionStoreState = SessionState;
|
|
6
|
-
|
|
7
|
-
export interface SessionState {
|
|
8
|
-
/**
|
|
9
|
-
* @title 当前活动的会话
|
|
10
|
-
* @description 当前正在编辑或查看的会话
|
|
11
|
-
*/
|
|
12
|
-
activeId: string;
|
|
13
|
-
isMobile?: boolean;
|
|
14
|
-
isSearching: boolean;
|
|
15
|
-
isSessionsFirstFetchFinished: boolean;
|
|
16
|
-
/**
|
|
17
|
-
* 后续看看是否可以将 router 部分的逻辑移出去
|
|
18
|
-
* @deprecated
|
|
19
|
-
*/
|
|
20
|
-
router?: AppRouterInstance;
|
|
21
|
-
searchKeywords: string;
|
|
22
|
-
searchSessions: LobeAgentSession[];
|
|
23
|
-
sessions: LobeAgentSession[];
|
|
24
|
-
}
|
|
4
|
+
export interface SessionStoreState extends SessionGroupState, SessionState {}
|
|
25
5
|
|
|
26
6
|
export const initialState: SessionStoreState = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
isSearching: false,
|
|
30
|
-
isSessionsFirstFetchFinished: false,
|
|
31
|
-
searchKeywords: '',
|
|
32
|
-
searchSessions: [],
|
|
33
|
-
sessions: [],
|
|
7
|
+
...initSessionGroupState,
|
|
8
|
+
...initialSessionState,
|
|
34
9
|
};
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { agentSelectors } from './slices/agent/selectors';
|
|
2
|
-
export {
|
|
2
|
+
export { sessionSelectors } from './slices/session/selectors';
|
|
3
|
+
export { sessionGroupSelectors } from './slices/sessionGroup/selectors';
|
|
@@ -17,7 +17,9 @@ vi.mock('@/services/session', () => ({
|
|
|
17
17
|
updateSessionGroup: vi.fn(),
|
|
18
18
|
removeSession: vi.fn(),
|
|
19
19
|
getSessions: vi.fn(),
|
|
20
|
+
updateSessionGroupId: vi.fn(),
|
|
20
21
|
searchSessions: vi.fn(),
|
|
22
|
+
updateSessionPinned: vi.fn(),
|
|
21
23
|
},
|
|
22
24
|
}));
|
|
23
25
|
|
|
@@ -133,4 +135,58 @@ describe('SessionAction', () => {
|
|
|
133
135
|
);
|
|
134
136
|
});
|
|
135
137
|
});
|
|
138
|
+
|
|
139
|
+
describe('activeSession', () => {
|
|
140
|
+
it('should set the provided session id as active', async () => {
|
|
141
|
+
const { result } = renderHook(() => useSessionStore());
|
|
142
|
+
const sessionId = 'active-session-id';
|
|
143
|
+
|
|
144
|
+
act(() => {
|
|
145
|
+
result.current.activeSession(sessionId);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
expect(result.current.activeId).toBe(sessionId);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('pinSession', () => {
|
|
153
|
+
it('should pin a session when pinned is true', async () => {
|
|
154
|
+
const { result } = renderHook(() => useSessionStore());
|
|
155
|
+
const sessionId = 'session-id-to-pin';
|
|
156
|
+
|
|
157
|
+
await act(async () => {
|
|
158
|
+
await result.current.pinSession(sessionId, true);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expect(sessionService.updateSessionPinned).toHaveBeenCalledWith(sessionId, true);
|
|
162
|
+
expect(mockRefresh).toHaveBeenCalled();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should unpin a session when pinned is false', async () => {
|
|
166
|
+
const { result } = renderHook(() => useSessionStore());
|
|
167
|
+
const sessionId = 'session-id-to-unpin';
|
|
168
|
+
|
|
169
|
+
await act(async () => {
|
|
170
|
+
await result.current.pinSession(sessionId, false);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
expect(sessionService.updateSessionPinned).toHaveBeenCalledWith(sessionId, false);
|
|
174
|
+
expect(mockRefresh).toHaveBeenCalled();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe('updateSessionGroupId', () => {
|
|
179
|
+
it('should update the session group and refresh the list', async () => {
|
|
180
|
+
const { result } = renderHook(() => useSessionStore());
|
|
181
|
+
const sessionId = 'session-id';
|
|
182
|
+
const groupId = 'new-group-id';
|
|
183
|
+
|
|
184
|
+
await act(async () => {
|
|
185
|
+
await result.current.updateSessionGroupId(sessionId, groupId);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
expect(sessionService.updateSessionGroupId).toHaveBeenCalledWith(sessionId, groupId);
|
|
189
|
+
expect(mockRefresh).toHaveBeenCalled();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
136
192
|
});
|
|
@@ -11,8 +11,8 @@ import { useGlobalStore } from '@/store/global';
|
|
|
11
11
|
import { settingsSelectors } from '@/store/global/selectors';
|
|
12
12
|
import { SessionStore } from '@/store/session';
|
|
13
13
|
import {
|
|
14
|
+
ChatSessionList,
|
|
14
15
|
LobeAgentSession,
|
|
15
|
-
LobeAgentSettings,
|
|
16
16
|
LobeSessionType,
|
|
17
17
|
LobeSessions,
|
|
18
18
|
} from '@/types/session';
|
|
@@ -42,12 +42,12 @@ export interface SessionAction {
|
|
|
42
42
|
* @param agent
|
|
43
43
|
* @returns sessionId
|
|
44
44
|
*/
|
|
45
|
-
createSession: (
|
|
45
|
+
createSession: (session?: DeepPartial<LobeAgentSession>) => Promise<string>;
|
|
46
46
|
duplicateSession: (id: string) => Promise<void>;
|
|
47
47
|
/**
|
|
48
48
|
* Pins or unpins a session.
|
|
49
49
|
*/
|
|
50
|
-
pinSession: (id: string, pinned
|
|
50
|
+
pinSession: (id: string, pinned: boolean) => Promise<void>;
|
|
51
51
|
/**
|
|
52
52
|
* re-fetch the data
|
|
53
53
|
*/
|
|
@@ -127,7 +127,7 @@ export const createSessionSlice: StateCreator<
|
|
|
127
127
|
},
|
|
128
128
|
|
|
129
129
|
pinSession: async (sessionId, pinned) => {
|
|
130
|
-
await sessionService.
|
|
130
|
+
await sessionService.updateSessionPinned(sessionId, pinned);
|
|
131
131
|
|
|
132
132
|
await get().refreshSessions();
|
|
133
133
|
},
|
|
@@ -144,6 +144,7 @@ export const createSessionSlice: StateCreator<
|
|
|
144
144
|
get().switchSession();
|
|
145
145
|
}
|
|
146
146
|
},
|
|
147
|
+
|
|
147
148
|
switchSession: (sessionId = INBOX_SESSION_ID) => {
|
|
148
149
|
const { isMobile, router } = get();
|
|
149
150
|
|
|
@@ -154,20 +155,23 @@ export const createSessionSlice: StateCreator<
|
|
|
154
155
|
},
|
|
155
156
|
|
|
156
157
|
useFetchSessions: () =>
|
|
157
|
-
useSWR<
|
|
158
|
+
useSWR<ChatSessionList>(FETCH_SESSIONS_KEY, sessionService.getSessionsWithGroup, {
|
|
158
159
|
onSuccess: (data) => {
|
|
159
160
|
// 由于 https://github.com/lobehub/lobe-chat/pull/541 的关系
|
|
160
161
|
// 只有触发了 refreshSessions 才会更新 sessions,进而触发页面 rerender
|
|
161
|
-
//
|
|
162
|
+
// 因此这里不能补充 equal 判断,否则会导致页面不更新
|
|
163
|
+
// if (get().isSessionsFirstFetchFinished && isEqual(get().sessions, data)) return;
|
|
164
|
+
|
|
162
165
|
// TODO:后续的根本解法应该是解除 inbox 和 session 的数据耦合
|
|
163
166
|
// 避免互相依赖的情况出现
|
|
164
167
|
|
|
165
|
-
// if (get().isSessionsFirstFetchFinished && isEqual(get().sessions, data)) return;
|
|
166
|
-
|
|
167
168
|
set(
|
|
168
169
|
{
|
|
170
|
+
customSessionGroups: data.customGroup,
|
|
171
|
+
defaultSessions: data.default,
|
|
169
172
|
isSessionsFirstFetchFinished: true,
|
|
170
|
-
|
|
173
|
+
pinnedSessions: data.pinned,
|
|
174
|
+
sessions: data.all,
|
|
171
175
|
},
|
|
172
176
|
false,
|
|
173
177
|
n('useFetchSessions/onSuccess', data),
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { LobeAgentSession, LobeSessions
|
|
1
|
+
import { LobeAgentSession, LobeSessions } from '@/types/session';
|
|
2
2
|
|
|
3
3
|
import { initLobeSession } from './initialState';
|
|
4
4
|
|
|
5
|
-
export const getSessionPinned = (session: LobeAgentSession) =>
|
|
6
|
-
session.group === SessionGroupDefaultKeys.Pinned;
|
|
5
|
+
export const getSessionPinned = (session: LobeAgentSession) => session.pinned;
|
|
7
6
|
|
|
8
7
|
const getSessionById = (id: string, sessions: LobeSessions): LobeAgentSession => {
|
|
9
8
|
const session = sessions.find((s) => s.id === id);
|
|
@@ -1,44 +1,52 @@
|
|
|
1
1
|
import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
|
|
2
2
|
|
|
3
|
-
import { DEFAULT_AGENT_META
|
|
3
|
+
import { DEFAULT_AGENT_META } from '@/const/meta';
|
|
4
4
|
import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
|
|
5
|
-
import { LobeAgentSession, LobeSessionType } from '@/types/session';
|
|
6
|
-
|
|
5
|
+
import { CustomSessionGroup, LobeAgentSession, LobeSessionType } from '@/types/session';
|
|
6
|
+
|
|
7
|
+
export const initLobeSession: LobeAgentSession = {
|
|
8
|
+
config: DEFAULT_AGENT_CONFIG,
|
|
9
|
+
createdAt: Date.now(),
|
|
10
|
+
id: '',
|
|
11
|
+
meta: DEFAULT_AGENT_META,
|
|
12
|
+
type: LobeSessionType.Agent,
|
|
13
|
+
updatedAt: Date.now(),
|
|
14
|
+
};
|
|
7
15
|
|
|
8
16
|
export interface SessionState {
|
|
9
17
|
/**
|
|
10
18
|
* @title 当前活动的会话
|
|
11
19
|
* @description 当前正在编辑或查看的会话
|
|
12
20
|
*/
|
|
13
|
-
activeId: string
|
|
14
|
-
|
|
21
|
+
activeId: string;
|
|
22
|
+
customSessionGroups: CustomSessionGroup[];
|
|
23
|
+
defaultSessions: LobeAgentSession[];
|
|
15
24
|
isMobile?: boolean;
|
|
25
|
+
isSearching: boolean;
|
|
26
|
+
isSessionsFirstFetchFinished: boolean;
|
|
27
|
+
pinnedSessions: LobeAgentSession[];
|
|
28
|
+
/**
|
|
29
|
+
* 后续看看是否可以将 router 部分的逻辑移出去
|
|
30
|
+
* @deprecated
|
|
31
|
+
*/
|
|
16
32
|
router?: AppRouterInstance;
|
|
17
33
|
searchKeywords: string;
|
|
34
|
+
searchSessions: LobeAgentSession[];
|
|
35
|
+
/**
|
|
36
|
+
* it means defaultSessions
|
|
37
|
+
*/
|
|
18
38
|
sessions: LobeAgentSession[];
|
|
19
|
-
topicSearchKeywords: string;
|
|
20
39
|
}
|
|
21
40
|
|
|
22
|
-
export const initLobeSession: LobeAgentSession = {
|
|
23
|
-
config: DEFAULT_AGENT_CONFIG,
|
|
24
|
-
createdAt: Date.now(),
|
|
25
|
-
id: '',
|
|
26
|
-
meta: DEFAULT_AGENT_META,
|
|
27
|
-
type: LobeSessionType.Agent,
|
|
28
|
-
updatedAt: Date.now(),
|
|
29
|
-
};
|
|
30
|
-
export const initInboxSession: LobeAgentSession = merge(initLobeSession, {
|
|
31
|
-
id: 'inbox',
|
|
32
|
-
meta: {
|
|
33
|
-
avatar: DEFAULT_INBOX_AVATAR,
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
|
|
37
41
|
export const initialSessionState: SessionState = {
|
|
38
42
|
activeId: 'inbox',
|
|
39
|
-
|
|
43
|
+
customSessionGroups: [],
|
|
44
|
+
defaultSessions: [],
|
|
40
45
|
isMobile: false,
|
|
46
|
+
isSearching: false,
|
|
47
|
+
isSessionsFirstFetchFinished: false,
|
|
48
|
+
pinnedSessions: [],
|
|
41
49
|
searchKeywords: '',
|
|
50
|
+
searchSessions: [],
|
|
42
51
|
sessions: [],
|
|
43
|
-
topicSearchKeywords: '',
|
|
44
52
|
};
|