@lobehub/chat 0.162.8 → 0.162.9
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/CHANGELOG.md +25 -0
- package/README.md +8 -8
- package/README.zh-CN.md +8 -8
- package/package.json +1 -1
- package/src/app/(main)/settings/common/features/Theme/index.tsx +1 -1
- package/src/features/Conversation/Error/AccessCodeForm.tsx +2 -2
- package/src/hooks/_header.ts +6 -6
- package/src/services/_auth.ts +2 -6
- package/src/services/_header.ts +2 -5
- package/src/services/user/client.ts +2 -1
- package/src/store/tool/slices/plugin/action.ts +1 -1
- package/src/store/user/slices/modelList/action.test.ts +31 -31
- package/src/store/user/slices/modelList/selectors/keyVaults.ts +10 -2
- package/src/store/user/slices/settings/action.test.ts +20 -9
- package/src/store/user/slices/settings/action.ts +18 -1
- package/src/store/user/slices/settings/initialState.ts +1 -0
- package/src/store/user/slices/settings/selectors/settings.ts +4 -7
- package/src/store/user/slices/sync/selectors.ts +8 -2
- package/src/types/user/settings/index.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.162.9](https://github.com/lobehub/lobe-chat/compare/v0.162.8...v0.162.9)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2024-05-29**</sup>
|
|
8
|
+
|
|
9
|
+
#### ♻ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **misc**: Refactor the settings to add optimistic updating.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Code refactoring
|
|
19
|
+
|
|
20
|
+
- **misc**: Refactor the settings to add optimistic updating, closes [#2709](https://github.com/lobehub/lobe-chat/issues/2709) ([fade53e](https://github.com/lobehub/lobe-chat/commit/fade53e))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
### [Version 0.162.8](https://github.com/lobehub/lobe-chat/compare/v0.162.7...v0.162.8)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2024-05-28**</sup>
|
package/README.md
CHANGED
|
@@ -262,14 +262,14 @@ Our marketplace is not just a showcase platform but also a collaborative space.
|
|
|
262
262
|
|
|
263
263
|
<!-- AGENT LIST -->
|
|
264
264
|
|
|
265
|
-
| Recent Submits | Description
|
|
266
|
-
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
267
|
-
| [Dart/Flutter Dev](https://chat-preview.lobehub.com/market?agent=dart-flutter)<br/><sup>By **[rezmeplxrf](https://github.com/rezmeplxrf)** on **2024-05-28**</sup> | Dart/Flutter Expert. Never nest more than 3 levels deep. Use riverpod, flutter_riverpod, riverpod_hook, flutter_hook for state management.<br/>`dart` `flutter` `development` `state-management` `riverpod`
|
|
268
|
-
| [C# .NET Technology Expert](https://chat-preview.lobehub.com/market?agent=dotnet-expert)<br/><sup>By **[johnnyqian](https://github.com/johnnyqian)** on **2024-05-28**</sup> | C# .NET Technology Expert<br/>`net` `developer` `net-core` `azure` `c` `microsoft` `sql-server` `entity-framework` `ef` `ef-core`
|
|
269
|
-
| [
|
|
270
|
-
| [
|
|
271
|
-
|
|
272
|
-
> 📊 Total agents: [<kbd>**
|
|
265
|
+
| Recent Submits | Description |
|
|
266
|
+
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
267
|
+
| [Dart/Flutter Dev](https://chat-preview.lobehub.com/market?agent=dart-flutter)<br/><sup>By **[rezmeplxrf](https://github.com/rezmeplxrf)** on **2024-05-28**</sup> | Dart/Flutter Expert. Never nest more than 3 levels deep. Use riverpod, flutter_riverpod, riverpod_hook, flutter_hook for state management.<br/>`dart` `flutter` `development` `state-management` `riverpod` |
|
|
268
|
+
| [C# .NET Technology Expert](https://chat-preview.lobehub.com/market?agent=dotnet-expert)<br/><sup>By **[johnnyqian](https://github.com/johnnyqian)** on **2024-05-28**</sup> | C# .NET Technology Expert<br/>`net` `developer` `net-core` `azure` `c` `microsoft` `sql-server` `entity-framework` `ef` `ef-core` |
|
|
269
|
+
| [Christian Missionary](https://chat-preview.lobehub.com/market?agent=jesus-missionary)<br/><sup>By **[epochaudio](https://github.com/epochaudio)** on **2024-05-28**</sup> | As a missionary of Jesus, I will enlighten your understanding and practical application of God's word based on the teachings of the Bible. Whether in times of confusion or seeking spiritual growth, I am here to serve you by this source of wisdom.<br/>`bible-teaching` `christian-mission` `theology-preaching` |
|
|
270
|
+
| [Daily Assistant](https://chat-preview.lobehub.com/market?agent=junior-helper)<br/><sup>By **[Qinks6](https://github.com/Qinks6)** on **2024-05-28**</sup> | A cute little helper that can search and draw<br/>`assistant` `search` `drawing` `information-retrieval` `user-interaction` |
|
|
271
|
+
|
|
272
|
+
> 📊 Total agents: [<kbd>**280**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
273
273
|
|
|
274
274
|
<!-- AGENT LIST -->
|
|
275
275
|
|
package/README.zh-CN.md
CHANGED
|
@@ -250,14 +250,14 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
|
|
250
250
|
|
|
251
251
|
<!-- AGENT LIST -->
|
|
252
252
|
|
|
253
|
-
| 最近新增 | 助手说明
|
|
254
|
-
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
255
|
-
| [Dart/Flutter Dev](https://chat-preview.lobehub.com/market?agent=dart-flutter)<br/><sup>By **[rezmeplxrf](https://github.com/rezmeplxrf)** on **2024-05-28**</sup> | Dart/Flutter 전문가. 3단계 이상 중첩하지 않음. 상태 관리에 riverpod, flutter_riverpod, riverpod_hook, flutter_hook 사용.<br/>`dart` `flutter` `개발` `상태-관리` `riverpod`
|
|
256
|
-
| [C# .NET 技术专家](https://chat-preview.lobehub.com/market?agent=dotnet-expert)<br/><sup>By **[johnnyqian](https://github.com/johnnyqian)** on **2024-05-28**</sup> | C# .NET 技术专家<br/>`net` `developer` `net-core` `azure` `c` `microsoft` `sql-server` `entity-framework` `ef` `ef-core`
|
|
257
|
-
| [
|
|
258
|
-
| [
|
|
259
|
-
|
|
260
|
-
> 📊 Total agents: [<kbd>**
|
|
253
|
+
| 最近新增 | 助手说明 |
|
|
254
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
255
|
+
| [Dart/Flutter Dev](https://chat-preview.lobehub.com/market?agent=dart-flutter)<br/><sup>By **[rezmeplxrf](https://github.com/rezmeplxrf)** on **2024-05-28**</sup> | Dart/Flutter 전문가. 3단계 이상 중첩하지 않음. 상태 관리에 riverpod, flutter_riverpod, riverpod_hook, flutter_hook 사용.<br/>`dart` `flutter` `개발` `상태-관리` `riverpod` |
|
|
256
|
+
| [C# .NET 技术专家](https://chat-preview.lobehub.com/market?agent=dotnet-expert)<br/><sup>By **[johnnyqian](https://github.com/johnnyqian)** on **2024-05-28**</sup> | C# .NET 技术专家<br/>`net` `developer` `net-core` `azure` `c` `microsoft` `sql-server` `entity-framework` `ef` `ef-core` |
|
|
257
|
+
| [基督传教士](https://chat-preview.lobehub.com/market?agent=jesus-missionary)<br/><sup>By **[epochaudio](https://github.com/epochaudio)** on **2024-05-28**</sup> | 作为一名耶稣传教士,我将依据圣经教导以启迪你对神的话语的理解和实际运用。无论是在困惑还是寻求灵性成长的过程中,我都在这智慧的源泉旁为你服务<br/>`圣经教学` `基督传教` `神学布道` |
|
|
258
|
+
| [日常小助手](https://chat-preview.lobehub.com/market?agent=junior-helper)<br/><sup>By **[Qinks6](https://github.com/Qinks6)** on **2024-05-28**</sup> | 一个能搜索、能画图的小可爱<br/>`助手` `搜索` `绘图` `信息查询` `用户交互` |
|
|
259
|
+
|
|
260
|
+
> 📊 Total agents: [<kbd>**280**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
261
261
|
|
|
262
262
|
<!-- AGENT LIST -->
|
|
263
263
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.162.
|
|
3
|
+
"version": "0.162.9",
|
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -41,7 +41,6 @@ const Theme = memo(() => {
|
|
|
41
41
|
{
|
|
42
42
|
children: (
|
|
43
43
|
<SelectWithImg
|
|
44
|
-
defaultValue={themeMode}
|
|
45
44
|
height={60}
|
|
46
45
|
onChange={setThemeMode}
|
|
47
46
|
options={[
|
|
@@ -65,6 +64,7 @@ const Theme = memo(() => {
|
|
|
65
64
|
},
|
|
66
65
|
]}
|
|
67
66
|
unoptimized={false}
|
|
67
|
+
value={themeMode}
|
|
68
68
|
width={100}
|
|
69
69
|
/>
|
|
70
70
|
),
|
|
@@ -5,7 +5,7 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
5
5
|
|
|
6
6
|
import { useChatStore } from '@/store/chat';
|
|
7
7
|
import { useUserStore } from '@/store/user';
|
|
8
|
-
import {
|
|
8
|
+
import { keyVaultsConfigSelectors } from '@/store/user/selectors';
|
|
9
9
|
|
|
10
10
|
import { FormAction } from './style';
|
|
11
11
|
|
|
@@ -16,7 +16,7 @@ interface AccessCodeFormProps {
|
|
|
16
16
|
const AccessCodeForm = memo<AccessCodeFormProps>(({ id }) => {
|
|
17
17
|
const { t } = useTranslation('error');
|
|
18
18
|
const [password, updateKeyVaults] = useUserStore((s) => [
|
|
19
|
-
|
|
19
|
+
keyVaultsConfigSelectors.password(s),
|
|
20
20
|
s.updateKeyVaults,
|
|
21
21
|
]);
|
|
22
22
|
const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
|
package/src/hooks/_header.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { LOBE_CHAT_ACCESS_CODE, OPENAI_API_KEY_HEADER_KEY, OPENAI_END_POINT } from '@/const/fetch';
|
|
2
2
|
import { useUserStore } from '@/store/user';
|
|
3
|
-
import {
|
|
4
|
-
keyVaultsConfigSelectors,
|
|
5
|
-
settingsSelectors,
|
|
6
|
-
} from '@/store/user/selectors';
|
|
3
|
+
import { keyVaultsConfigSelectors } from '@/store/user/selectors';
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
/**
|
|
6
|
+
* TODO: Need to be removed after tts refactor
|
|
7
|
+
* @deprecated
|
|
8
|
+
*/
|
|
9
9
|
// eslint-disable-next-line no-undef
|
|
10
10
|
export const createHeaderWithOpenAI = (header?: HeadersInit): HeadersInit => {
|
|
11
11
|
const openai = keyVaultsConfigSelectors.openAIConfig(useUserStore.getState());
|
|
@@ -16,7 +16,7 @@ export const createHeaderWithOpenAI = (header?: HeadersInit): HeadersInit => {
|
|
|
16
16
|
// eslint-disable-next-line no-undef
|
|
17
17
|
return {
|
|
18
18
|
...header,
|
|
19
|
-
[LOBE_CHAT_ACCESS_CODE]:
|
|
19
|
+
[LOBE_CHAT_ACCESS_CODE]: keyVaultsConfigSelectors.password(useUserStore.getState()),
|
|
20
20
|
[OPENAI_API_KEY_HEADER_KEY]: apiKey,
|
|
21
21
|
[OPENAI_END_POINT]: endpoint,
|
|
22
22
|
};
|
package/src/services/_auth.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { JWTPayload, LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
|
|
2
2
|
import { ModelProvider } from '@/libs/agent-runtime';
|
|
3
3
|
import { useUserStore } from '@/store/user';
|
|
4
|
-
import {
|
|
5
|
-
keyVaultsConfigSelectors,
|
|
6
|
-
settingsSelectors,
|
|
7
|
-
userProfileSelectors,
|
|
8
|
-
} from '@/store/user/selectors';
|
|
4
|
+
import { keyVaultsConfigSelectors, userProfileSelectors } from '@/store/user/selectors';
|
|
9
5
|
import { GlobalLLMProviderKey } from '@/types/user/settings';
|
|
10
6
|
import { createJWT } from '@/utils/jwt';
|
|
11
7
|
|
|
@@ -51,7 +47,7 @@ export const getProviderAuthPayload = (provider: string) => {
|
|
|
51
47
|
};
|
|
52
48
|
|
|
53
49
|
const createAuthTokenWithPayload = async (payload = {}) => {
|
|
54
|
-
const accessCode =
|
|
50
|
+
const accessCode = keyVaultsConfigSelectors.password(useUserStore.getState());
|
|
55
51
|
const userId = userProfileSelectors.userId(useUserStore.getState());
|
|
56
52
|
|
|
57
53
|
return await createJWT<JWTPayload>({ accessCode, userId, ...payload });
|
package/src/services/_header.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { LOBE_CHAT_ACCESS_CODE, OPENAI_API_KEY_HEADER_KEY, OPENAI_END_POINT } from '@/const/fetch';
|
|
2
2
|
import { useUserStore } from '@/store/user';
|
|
3
|
-
import {
|
|
4
|
-
keyVaultsConfigSelectors,
|
|
5
|
-
settingsSelectors,
|
|
6
|
-
} from '@/store/user/selectors';
|
|
3
|
+
import { keyVaultsConfigSelectors } from '@/store/user/selectors';
|
|
7
4
|
|
|
8
5
|
/**
|
|
9
6
|
* TODO: Need to be removed after tts refactor
|
|
@@ -16,7 +13,7 @@ export const createHeaderWithOpenAI = (header?: HeadersInit): HeadersInit => {
|
|
|
16
13
|
// eslint-disable-next-line no-undef
|
|
17
14
|
return {
|
|
18
15
|
...header,
|
|
19
|
-
[LOBE_CHAT_ACCESS_CODE]:
|
|
16
|
+
[LOBE_CHAT_ACCESS_CODE]: keyVaultsConfigSelectors.password(useUserStore.getState()),
|
|
20
17
|
[OPENAI_API_KEY_HEADER_KEY]: openAIConfig.apiKey || '',
|
|
21
18
|
[OPENAI_END_POINT]: openAIConfig.baseURL || '',
|
|
22
19
|
};
|
|
@@ -33,7 +33,8 @@ export class ClientService implements IUserService {
|
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
37
|
+
updateUserSettings = async (patch: DeepPartial<UserSettings>, _?: any) => {
|
|
37
38
|
return UserModel.updateSettings(patch);
|
|
38
39
|
};
|
|
39
40
|
|
|
@@ -53,7 +53,7 @@ export const createPluginSlice: StateCreator<
|
|
|
53
53
|
const previousSettings = pluginSelectors.getPluginSettingsById(id)(get());
|
|
54
54
|
const nextSettings = merge(previousSettings, settings);
|
|
55
55
|
|
|
56
|
-
set({ updatePluginSettingsSignal: newSignal });
|
|
56
|
+
set({ updatePluginSettingsSignal: newSignal }, false, 'create new Signal');
|
|
57
57
|
await pluginService.updatePluginSettings(id, nextSettings, newSignal.signal);
|
|
58
58
|
|
|
59
59
|
await get().refreshPlugins();
|
|
@@ -32,11 +32,10 @@ describe('LLMSettingsSliceAction', () => {
|
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
// Assert that updateUserSettings was called with the correct OpenAI configuration
|
|
35
|
-
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
36
|
-
languageModel: {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
});
|
|
35
|
+
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
36
|
+
{ languageModel: { openai: openAIConfig } },
|
|
37
|
+
expect.any(AbortSignal),
|
|
38
|
+
);
|
|
40
39
|
});
|
|
41
40
|
});
|
|
42
41
|
|
|
@@ -212,11 +211,10 @@ describe('LLMSettingsSliceAction', () => {
|
|
|
212
211
|
await result.current.removeEnabledModels('azure', model);
|
|
213
212
|
});
|
|
214
213
|
|
|
215
|
-
expect(spyOn).toHaveBeenCalledWith(
|
|
216
|
-
languageModel: {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
});
|
|
214
|
+
expect(spyOn).toHaveBeenCalledWith(
|
|
215
|
+
{ languageModel: { azure: { enabledModels: ['gpt-4'] } } },
|
|
216
|
+
expect.any(AbortSignal),
|
|
217
|
+
);
|
|
220
218
|
});
|
|
221
219
|
});
|
|
222
220
|
|
|
@@ -250,11 +248,10 @@ describe('LLMSettingsSliceAction', () => {
|
|
|
250
248
|
await result.current.toggleProviderEnabled('minimax', true);
|
|
251
249
|
});
|
|
252
250
|
|
|
253
|
-
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
254
|
-
languageModel: {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
});
|
|
251
|
+
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
252
|
+
{ languageModel: { minimax: { enabled: true } } },
|
|
253
|
+
expect.any(AbortSignal),
|
|
254
|
+
);
|
|
258
255
|
});
|
|
259
256
|
|
|
260
257
|
it('should disable the provider', async () => {
|
|
@@ -265,11 +262,10 @@ describe('LLMSettingsSliceAction', () => {
|
|
|
265
262
|
await result.current.toggleProviderEnabled(provider, false);
|
|
266
263
|
});
|
|
267
264
|
|
|
268
|
-
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
269
|
-
languageModel: {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
});
|
|
265
|
+
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
266
|
+
{ languageModel: { openai: { enabled: false } } },
|
|
267
|
+
expect.any(AbortSignal),
|
|
268
|
+
);
|
|
273
269
|
});
|
|
274
270
|
});
|
|
275
271
|
|
|
@@ -285,15 +281,18 @@ describe('LLMSettingsSliceAction', () => {
|
|
|
285
281
|
await result.current.updateEnabledModels(provider, modelKeys, options);
|
|
286
282
|
});
|
|
287
283
|
|
|
288
|
-
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
284
|
+
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
285
|
+
{
|
|
286
|
+
languageModel: {
|
|
287
|
+
openai: {
|
|
288
|
+
customModelCards: [{ id: 'custom-model' }],
|
|
289
|
+
// TODO:目标单测中需要包含下面这一行
|
|
290
|
+
// enabledModels: ['gpt-3.5-turbo', 'custom-model'],
|
|
291
|
+
},
|
|
294
292
|
},
|
|
295
293
|
},
|
|
296
|
-
|
|
294
|
+
expect.any(AbortSignal),
|
|
295
|
+
);
|
|
297
296
|
});
|
|
298
297
|
|
|
299
298
|
it('should not add removed model to customModelCards', async () => {
|
|
@@ -316,11 +315,12 @@ describe('LLMSettingsSliceAction', () => {
|
|
|
316
315
|
await result.current.updateEnabledModels(provider, modelKeys, options);
|
|
317
316
|
});
|
|
318
317
|
|
|
319
|
-
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
320
|
-
|
|
321
|
-
openai: { enabledModels: ['gpt-3.5-turbo'] },
|
|
318
|
+
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
319
|
+
{
|
|
320
|
+
languageModel: { openai: { enabledModels: ['gpt-3.5-turbo'] } },
|
|
322
321
|
},
|
|
323
|
-
|
|
322
|
+
expect.any(AbortSignal),
|
|
323
|
+
);
|
|
324
324
|
});
|
|
325
325
|
});
|
|
326
326
|
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { UserStore } from '@/store/user';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
GlobalLLMProviderKey,
|
|
4
|
+
OpenAICompatibleKeyVault,
|
|
5
|
+
UserKeyVaults,
|
|
6
|
+
} from '@/types/user/settings';
|
|
3
7
|
|
|
4
8
|
import { currentSettings } from '../../settings/selectors/settings';
|
|
5
9
|
|
|
6
|
-
export const keyVaultsSettings = (s: UserStore) =>
|
|
10
|
+
export const keyVaultsSettings = (s: UserStore): UserKeyVaults =>
|
|
11
|
+
currentSettings(s).keyVaults || {};
|
|
7
12
|
|
|
8
13
|
const openAIConfig = (s: UserStore) => keyVaultsSettings(s).openai || {};
|
|
9
14
|
const bedrockConfig = (s: UserStore) => keyVaultsSettings(s).bedrock || {};
|
|
@@ -15,6 +20,8 @@ const getVaultByProvider = (provider: GlobalLLMProviderKey) => (s: UserStore) =>
|
|
|
15
20
|
const isProviderEndpointNotEmpty = (provider: string) => (s: UserStore) =>
|
|
16
21
|
!!getVaultByProvider(provider as GlobalLLMProviderKey)(s)?.baseURL;
|
|
17
22
|
|
|
23
|
+
const password = (s: UserStore) => keyVaultsSettings(s).password || '';
|
|
24
|
+
|
|
18
25
|
export const keyVaultsConfigSelectors = {
|
|
19
26
|
azureConfig,
|
|
20
27
|
bedrockConfig,
|
|
@@ -22,4 +29,5 @@ export const keyVaultsConfigSelectors = {
|
|
|
22
29
|
isProviderEndpointNotEmpty,
|
|
23
30
|
ollamaConfig,
|
|
24
31
|
openAIConfig,
|
|
32
|
+
password,
|
|
25
33
|
};
|
|
@@ -40,9 +40,10 @@ describe('SettingsAction', () => {
|
|
|
40
40
|
expect(setSettingsSpy).toHaveBeenCalledWith(newSettings);
|
|
41
41
|
|
|
42
42
|
// Assert that the state has been updated
|
|
43
|
-
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
44
|
-
general: { themeMode: 'dark' },
|
|
45
|
-
|
|
43
|
+
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
44
|
+
{ general: { themeMode: 'dark' } },
|
|
45
|
+
expect.any(AbortSignal),
|
|
46
|
+
);
|
|
46
47
|
|
|
47
48
|
// Restore the spy
|
|
48
49
|
setSettingsSpy.mockRestore();
|
|
@@ -77,7 +78,10 @@ describe('SettingsAction', () => {
|
|
|
77
78
|
});
|
|
78
79
|
|
|
79
80
|
// Assert that updateUserSettings was called with the correct settings
|
|
80
|
-
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
81
|
+
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
82
|
+
partialSettings,
|
|
83
|
+
expect.any(AbortSignal),
|
|
84
|
+
);
|
|
81
85
|
});
|
|
82
86
|
});
|
|
83
87
|
|
|
@@ -92,9 +96,10 @@ describe('SettingsAction', () => {
|
|
|
92
96
|
});
|
|
93
97
|
|
|
94
98
|
// Assert that updateUserSettings was called with the correct theme mode
|
|
95
|
-
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
96
|
-
general: { themeMode },
|
|
97
|
-
|
|
99
|
+
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
100
|
+
{ general: { themeMode } },
|
|
101
|
+
expect.any(AbortSignal),
|
|
102
|
+
);
|
|
98
103
|
});
|
|
99
104
|
});
|
|
100
105
|
|
|
@@ -111,7 +116,10 @@ describe('SettingsAction', () => {
|
|
|
111
116
|
});
|
|
112
117
|
|
|
113
118
|
// Assert that updateUserSettings was called with the merged agent settings
|
|
114
|
-
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
119
|
+
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
120
|
+
{ defaultAgent: updatedAgent },
|
|
121
|
+
expect.any(AbortSignal),
|
|
122
|
+
);
|
|
115
123
|
});
|
|
116
124
|
});
|
|
117
125
|
|
|
@@ -136,7 +144,10 @@ describe('SettingsAction', () => {
|
|
|
136
144
|
});
|
|
137
145
|
|
|
138
146
|
// Assert that updateUserSettings was called with the correct settings
|
|
139
|
-
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
147
|
+
expect(userService.updateUserSettings).toHaveBeenCalledWith(
|
|
148
|
+
systemAgentSettings,
|
|
149
|
+
expect.any(AbortSignal),
|
|
150
|
+
);
|
|
140
151
|
});
|
|
141
152
|
});
|
|
142
153
|
});
|
|
@@ -14,6 +14,8 @@ import { merge } from '@/utils/merge';
|
|
|
14
14
|
|
|
15
15
|
export interface UserSettingsAction {
|
|
16
16
|
importAppSettings: (settings: UserSettings) => Promise<void>;
|
|
17
|
+
internal_createSignal: () => AbortController;
|
|
18
|
+
|
|
17
19
|
resetSettings: () => Promise<void>;
|
|
18
20
|
setSettings: (settings: DeepPartial<UserSettings>) => Promise<void>;
|
|
19
21
|
switchLocale: (locale: LocaleMode) => Promise<void>;
|
|
@@ -21,6 +23,7 @@ export interface UserSettingsAction {
|
|
|
21
23
|
updateDefaultAgent: (agent: DeepPartial<LobeAgentSettings>) => Promise<void>;
|
|
22
24
|
updateGeneralConfig: (settings: Partial<UserGeneralConfig>) => Promise<void>;
|
|
23
25
|
updateKeyVaults: (settings: Partial<UserKeyVaults>) => Promise<void>;
|
|
26
|
+
|
|
24
27
|
updateSystemAgent: (key: string, value: { model: string; provider: string }) => Promise<void>;
|
|
25
28
|
}
|
|
26
29
|
|
|
@@ -35,6 +38,18 @@ export const createSettingsSlice: StateCreator<
|
|
|
35
38
|
|
|
36
39
|
await setSettings(importAppSettings);
|
|
37
40
|
},
|
|
41
|
+
|
|
42
|
+
internal_createSignal: () => {
|
|
43
|
+
const abortController = get().updateSettingsSignal;
|
|
44
|
+
if (abortController && !abortController.signal.aborted) abortController.abort('canceled');
|
|
45
|
+
|
|
46
|
+
const newSignal = new AbortController();
|
|
47
|
+
|
|
48
|
+
set({ updateSettingsSignal: newSignal }, false, 'signalForUpdateSettings');
|
|
49
|
+
|
|
50
|
+
return newSignal;
|
|
51
|
+
},
|
|
52
|
+
|
|
38
53
|
resetSettings: async () => {
|
|
39
54
|
await userService.resetUserSettings();
|
|
40
55
|
await get().refreshUserState();
|
|
@@ -47,8 +62,10 @@ export const createSettingsSlice: StateCreator<
|
|
|
47
62
|
if (isEqual(prevSetting, nextSettings)) return;
|
|
48
63
|
|
|
49
64
|
const diffs = difference(nextSettings, defaultSettings);
|
|
65
|
+
set({ settings: diffs }, false, 'optimistic_updateSettings');
|
|
50
66
|
|
|
51
|
-
|
|
67
|
+
const abortController = get().internal_createSignal();
|
|
68
|
+
await userService.updateUserSettings(diffs, abortController.signal);
|
|
52
69
|
await get().refreshUserState();
|
|
53
70
|
},
|
|
54
71
|
switchLocale: async (locale) => {
|
|
@@ -6,6 +6,7 @@ import { UserSettings } from '@/types/user/settings';
|
|
|
6
6
|
export interface UserSettingsState {
|
|
7
7
|
defaultSettings: UserSettings;
|
|
8
8
|
settings: DeepPartial<UserSettings>;
|
|
9
|
+
updateSettingsSignal?: AbortController;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export const initialSettingsState: UserSettingsState = {
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
GlobalLLMProviderKey,
|
|
10
10
|
ProviderConfig,
|
|
11
|
+
UserModelProviderConfig,
|
|
11
12
|
UserSettings,
|
|
12
13
|
} from '@/types/user/settings';
|
|
13
14
|
import { merge } from '@/utils/merge';
|
|
@@ -16,14 +17,11 @@ import { UserStore } from '../../../store';
|
|
|
16
17
|
|
|
17
18
|
export const currentSettings = (s: UserStore): UserSettings => merge(s.defaultSettings, s.settings);
|
|
18
19
|
|
|
19
|
-
export const currentLLMSettings = (s: UserStore) =>
|
|
20
|
+
export const currentLLMSettings = (s: UserStore): UserModelProviderConfig =>
|
|
21
|
+
currentSettings(s).languageModel || {};
|
|
20
22
|
|
|
21
23
|
export const getProviderConfigById = (provider: string) => (s: UserStore) =>
|
|
22
|
-
currentLLMSettings(s)[provider as GlobalLLMProviderKey] as
|
|
23
|
-
| ProviderConfig
|
|
24
|
-
| undefined;
|
|
25
|
-
|
|
26
|
-
const password = (s: UserStore) => currentSettings(s).keyVaults.password || '';
|
|
24
|
+
currentLLMSettings(s)[provider as GlobalLLMProviderKey] as ProviderConfig | undefined;
|
|
27
25
|
|
|
28
26
|
const currentTTS = (s: UserStore) => merge(DEFAULT_TTS_CONFIG, currentSettings(s).tts);
|
|
29
27
|
|
|
@@ -50,6 +48,5 @@ export const settingsSelectors = {
|
|
|
50
48
|
defaultAgentMeta,
|
|
51
49
|
exportSettings,
|
|
52
50
|
isDalleAutoGenerating,
|
|
53
|
-
password,
|
|
54
51
|
providerConfig: getProviderConfigById,
|
|
55
52
|
};
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import { DEFAULT_SYNC_CONFIG } from '@/const/settings/sync';
|
|
2
|
+
import { UserSyncSettings } from '@/types/user/settings';
|
|
3
|
+
|
|
1
4
|
import { UserStore } from '../../store';
|
|
2
5
|
import { currentSettings } from '../settings/selectors/settings';
|
|
3
6
|
|
|
4
|
-
const
|
|
7
|
+
const syncConfig = (s: UserStore): UserSyncSettings =>
|
|
8
|
+
currentSettings(s).sync || DEFAULT_SYNC_CONFIG;
|
|
9
|
+
|
|
10
|
+
const webrtcConfig = (s: UserStore) => syncConfig(s).webrtc;
|
|
5
11
|
const webrtcChannelName = (s: UserStore) => webrtcConfig(s).channelName;
|
|
6
12
|
const enableWebRTC = (s: UserStore) => webrtcConfig(s).enabled;
|
|
7
|
-
const deviceName = (s: UserStore) =>
|
|
13
|
+
const deviceName = (s: UserStore) => syncConfig(s).deviceName;
|
|
8
14
|
|
|
9
15
|
export const syncSettingsSelectors = {
|
|
10
16
|
deviceName,
|
|
@@ -25,7 +25,7 @@ export interface UserSettings {
|
|
|
25
25
|
general: UserGeneralConfig;
|
|
26
26
|
keyVaults: UserKeyVaults;
|
|
27
27
|
languageModel: UserModelProviderConfig;
|
|
28
|
-
sync
|
|
28
|
+
sync?: UserSyncSettings;
|
|
29
29
|
systemAgent: UserSystemAgentConfig;
|
|
30
30
|
tool: UserToolConfig;
|
|
31
31
|
tts: UserTTSConfig;
|