@lobehub/chat 0.161.21 → 0.161.23
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 +58 -0
- package/package.json +1 -1
- package/src/app/(main)/chat/settings/features/SubmitAgentButton/SubmitAgentModal.tsx +3 -3
- package/src/app/(main)/settings/common/features/Common.tsx +1 -1
- package/src/app/(main)/settings/common/features/Theme/ThemeSwatches/ThemeSwatchesNeutral.tsx +5 -5
- package/src/app/(main)/settings/common/features/Theme/ThemeSwatches/ThemeSwatchesPrimary.tsx +5 -5
- package/src/app/(main)/settings/common/features/Theme/index.tsx +5 -4
- package/src/app/(main)/settings/llm/Azure/index.tsx +4 -4
- package/src/app/(main)/settings/llm/Bedrock/index.tsx +5 -5
- package/src/app/(main)/settings/llm/Ollama/Checker.tsx +1 -1
- package/src/app/(main)/settings/llm/components/Checker.tsx +10 -6
- package/src/app/(main)/settings/llm/components/ProviderConfig/index.tsx +6 -5
- package/src/app/(main)/settings/llm/components/ProviderModelList/CustomModelOption.tsx +1 -1
- package/src/app/(main)/settings/llm/components/ProviderModelList/ModelFetcher.tsx +1 -1
- package/src/app/(main)/settings/llm/components/ProviderModelList/Option.tsx +1 -1
- package/src/app/(main)/settings/llm/components/ProviderModelList/index.tsx +1 -1
- package/src/app/(main)/settings/llm/const.ts +2 -1
- package/src/const/settings/agent.ts +2 -2
- package/src/const/settings/common.ts +2 -2
- package/src/const/settings/index.ts +4 -3
- package/src/const/settings/llm.ts +2 -21
- package/src/const/settings/sync.ts +2 -2
- package/src/const/settings/systemAgent.ts +2 -2
- package/src/const/settings/tts.ts +2 -2
- package/src/database/client/core/db.ts +19 -0
- package/src/database/client/core/migrations/migrateSettingsToUser/type.ts +1 -1
- package/src/database/client/core/schemas.ts +3 -2
- package/src/database/client/models/__tests__/user.test.ts +5 -5
- package/src/database/client/models/user.ts +2 -3
- package/src/database/client/schemas/user.ts +10 -4
- package/src/features/AgentSetting/AgentMeta/index.tsx +3 -3
- package/src/features/AgentSetting/AgentTTS/index.tsx +2 -2
- package/src/features/ChatInput/STT/browser.tsx +2 -2
- package/src/features/ChatInput/STT/openai.tsx +2 -2
- package/src/features/Conversation/Error/APIKeyForm/Bedrock.tsx +5 -5
- package/src/features/Conversation/Error/APIKeyForm/ProviderApiKeyForm.tsx +5 -5
- package/src/features/Conversation/Error/APIKeyForm/index.tsx +1 -1
- package/src/features/Conversation/Error/AccessCodeForm.tsx +4 -4
- package/src/features/Conversation/Extras/TTS/index.tsx +2 -2
- package/src/features/Conversation/Plugins/Render/MarkdownType/index.tsx +2 -2
- package/src/features/Conversation/components/BubblesLoading.tsx +49 -41
- package/src/features/Conversation/components/ChatItem/index.tsx +2 -2
- package/src/features/PluginDevModal/LocalForm.tsx +2 -2
- package/src/features/PluginStore/index.tsx +6 -2
- package/src/features/User/UserPanel/LangButton.tsx +2 -2
- package/src/features/User/UserPanel/ThemeButton.tsx +2 -2
- package/src/hooks/_header.ts +6 -3
- package/src/hooks/useTTS.ts +2 -2
- package/src/layout/GlobalProvider/AppTheme.tsx +4 -4
- package/src/libs/agent-runtime/types/type.ts +3 -2
- package/src/migrations/FromV6ToV7/fixtures/output-v7-from-v1.json +203 -0
- package/src/migrations/FromV6ToV7/fixtures/provider-input-v6.json +103 -0
- package/src/migrations/FromV6ToV7/fixtures/provider-output-v7.json +118 -0
- package/src/migrations/FromV6ToV7/index.ts +101 -0
- package/src/migrations/FromV6ToV7/migrations.test.ts +64 -0
- package/src/migrations/FromV6ToV7/types/v6.ts +61 -0
- package/src/migrations/FromV6ToV7/types/v7.ts +71 -0
- package/src/migrations/index.ts +9 -3
- package/src/services/__tests__/chat.test.ts +19 -19
- package/src/services/__tests__/share.test.ts +4 -4
- package/src/services/_auth.test.ts +10 -5
- package/src/services/_auth.ts +11 -7
- package/src/services/_header.ts +6 -3
- package/src/services/config.ts +2 -2
- package/src/services/ollama.ts +3 -3
- package/src/services/share.ts +3 -3
- package/src/services/user/client.test.ts +3 -3
- package/src/services/user/client.ts +3 -3
- package/src/services/user/type.ts +2 -2
- package/src/store/agent/store.ts +4 -9
- package/src/store/chat/store.ts +4 -8
- package/src/store/file/store.ts +3 -9
- package/src/store/global/store.ts +5 -8
- package/src/store/market/store.ts +5 -8
- package/src/store/middleware/createDevtools.ts +23 -0
- package/src/store/serverConfig/store.ts +5 -11
- package/src/store/session/store.ts +3 -1
- package/src/store/tool/store.ts +4 -9
- package/src/store/user/helpers.ts +3 -2
- package/src/store/user/selectors.ts +10 -2
- package/src/store/user/slices/common/action.test.ts +3 -3
- package/src/store/user/slices/common/action.ts +4 -4
- package/src/store/user/slices/modelList/action.test.ts +2 -2
- package/src/store/user/slices/modelList/action.ts +16 -4
- package/src/store/user/slices/modelList/selectors/index.ts +1 -0
- package/src/store/user/slices/modelList/selectors/keyVaults.ts +25 -0
- package/src/store/user/slices/modelList/selectors/modelConfig.test.ts +1 -1
- package/src/store/user/slices/modelList/selectors/modelConfig.ts +1 -6
- package/src/store/user/slices/modelList/selectors/modelProvider.ts +1 -1
- package/src/store/user/slices/settings/action.test.ts +16 -14
- package/src/store/user/slices/settings/action.ts +14 -8
- package/src/store/user/slices/settings/initialState.ts +3 -3
- package/src/store/user/slices/settings/selectors/general.test.ts +45 -0
- package/src/store/user/slices/settings/selectors/general.ts +40 -0
- package/src/store/user/slices/settings/selectors/index.ts +1 -0
- package/src/store/user/slices/settings/selectors/settings.test.ts +0 -39
- package/src/store/user/slices/settings/selectors/settings.ts +11 -34
- package/src/store/user/store.ts +5 -8
- package/src/types/exportConfig.ts +2 -2
- package/src/types/serverConfig.ts +2 -2
- package/src/types/user/index.ts +2 -2
- package/src/types/{settings/base.ts → user/settings/general.ts} +4 -1
- package/src/types/user/settings/index.ts +32 -0
- package/src/types/user/settings/keyVaults.ts +36 -0
- package/src/types/user/settings/modelProvider.ts +34 -0
- package/src/types/{settings → user/settings}/sync.ts +1 -1
- package/src/types/{settings → user/settings}/systemAgent.ts +1 -1
- package/src/types/user/settings/tool.ts +5 -0
- package/src/types/{settings → user/settings}/tts.ts +1 -1
- package/src/types/settings/index.ts +0 -33
- package/src/types/settings/modelProvider.ts +0 -62
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { PersistOptions,
|
|
1
|
+
import { PersistOptions, persist } from 'zustand/middleware';
|
|
2
2
|
import { shallow } from 'zustand/shallow';
|
|
3
3
|
import { createWithEqualityFn } from 'zustand/traditional';
|
|
4
4
|
import type { StateCreator } from 'zustand/vanilla';
|
|
5
5
|
|
|
6
6
|
import { createHyperStorage } from '@/store/middleware/createHyperStorage';
|
|
7
|
-
import { isDev } from '@/utils/env';
|
|
8
7
|
|
|
8
|
+
import { createDevtools } from '../middleware/createDevtools';
|
|
9
9
|
import { type StoreAction, createMarketAction } from './action';
|
|
10
10
|
import { type StoreState, initialState } from './initialState';
|
|
11
11
|
|
|
@@ -40,12 +40,9 @@ const createStore: StateCreator<Store, [['zustand/devtools', never]]> = (...para
|
|
|
40
40
|
...createMarketAction(...parameters),
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
+
const devtools = createDevtools('market');
|
|
44
|
+
|
|
43
45
|
export const useMarketStore = createWithEqualityFn<Store>()(
|
|
44
|
-
persist(
|
|
45
|
-
devtools(createStore, {
|
|
46
|
-
name: LOBE_AGENT_MARKET + (isDev ? '_DEV' : ''),
|
|
47
|
-
}),
|
|
48
|
-
persistOptions,
|
|
49
|
-
),
|
|
46
|
+
persist(devtools(createStore), persistOptions),
|
|
50
47
|
shallow,
|
|
51
48
|
);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { optionalDevtools } from 'zustand-utils';
|
|
2
|
+
import { devtools as _devtools } from 'zustand/middleware';
|
|
3
|
+
|
|
4
|
+
import { isDev } from '@/utils/env';
|
|
5
|
+
|
|
6
|
+
export const createDevtools =
|
|
7
|
+
(name: string): typeof _devtools =>
|
|
8
|
+
(initializer) => {
|
|
9
|
+
let showDevtools = false;
|
|
10
|
+
|
|
11
|
+
// check url to show devtools
|
|
12
|
+
if (typeof window !== 'undefined') {
|
|
13
|
+
const url = new URL(window.location.href);
|
|
14
|
+
const debug = url.searchParams.get('debug');
|
|
15
|
+
if (debug?.includes(name)) {
|
|
16
|
+
showDevtools = true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return optionalDevtools(showDevtools)(initializer, {
|
|
21
|
+
name: `LobeChat_${name}` + (isDev ? '_DEV' : ''),
|
|
22
|
+
});
|
|
23
|
+
};
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { StoreApi } from 'zustand';
|
|
2
2
|
import { createContext } from 'zustand-utils';
|
|
3
|
-
import { devtools } from 'zustand/middleware';
|
|
4
3
|
import { shallow } from 'zustand/shallow';
|
|
5
4
|
import { createWithEqualityFn } from 'zustand/traditional';
|
|
6
5
|
import { StateCreator } from 'zustand/vanilla';
|
|
7
6
|
|
|
8
7
|
import { DEFAULT_FEATURE_FLAGS, IFeatureFlags } from '@/config/featureFlags';
|
|
8
|
+
import { createDevtools } from '@/store/middleware/createDevtools';
|
|
9
9
|
import { GlobalServerConfig } from '@/types/serverConfig';
|
|
10
|
-
import { isDev } from '@/utils/env';
|
|
11
10
|
import { merge } from '@/utils/merge';
|
|
12
11
|
import { StoreApiWithSelector } from '@/utils/zustand';
|
|
13
12
|
|
|
@@ -42,21 +41,16 @@ declare global {
|
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
43
|
|
|
44
|
+
const devtools = createDevtools('serverConfig');
|
|
45
|
+
|
|
45
46
|
export const initServerConfigStore = (initState: Partial<ServerConfigStore>) =>
|
|
46
|
-
createWithEqualityFn<ServerConfigStore>()(
|
|
47
|
-
devtools(createStore(initState || {}), {
|
|
48
|
-
name: 'LobeChat_ServerConfig' + (isDev ? '_DEV' : ''),
|
|
49
|
-
}),
|
|
50
|
-
shallow,
|
|
51
|
-
);
|
|
47
|
+
createWithEqualityFn<ServerConfigStore>()(devtools(createStore(initState || {})), shallow);
|
|
52
48
|
|
|
53
49
|
export const createServerConfigStore = (initState?: Partial<ServerConfigStore>) => {
|
|
54
50
|
// make sure there is only one store
|
|
55
51
|
if (!store) {
|
|
56
52
|
store = createWithEqualityFn<ServerConfigStore>()(
|
|
57
|
-
devtools(createStore(initState || {}),
|
|
58
|
-
name: 'LobeChat_ServerConfig' + (isDev ? '_DEV' : ''),
|
|
59
|
-
}),
|
|
53
|
+
devtools(createStore(initState || {})),
|
|
60
54
|
shallow,
|
|
61
55
|
);
|
|
62
56
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { subscribeWithSelector } from 'zustand/middleware';
|
|
2
2
|
import { shallow } from 'zustand/shallow';
|
|
3
3
|
import { createWithEqualityFn } from 'zustand/traditional';
|
|
4
4
|
import { StateCreator } from 'zustand/vanilla';
|
|
5
5
|
|
|
6
6
|
import { isDev } from '@/utils/env';
|
|
7
7
|
|
|
8
|
+
import { createDevtools } from '../middleware/createDevtools';
|
|
8
9
|
import { SessionStoreState, initialState } from './initialState';
|
|
9
10
|
import { SessionAction, createSessionSlice } from './slices/session/action';
|
|
10
11
|
import { SessionGroupAction, createSessionGroupSlice } from './slices/sessionGroup/action';
|
|
@@ -20,6 +21,7 @@ const createStore: StateCreator<SessionStore, [['zustand/devtools', never]]> = (
|
|
|
20
21
|
});
|
|
21
22
|
|
|
22
23
|
// =============== implement useStore ============ //
|
|
24
|
+
const devtools = createDevtools('session');
|
|
23
25
|
|
|
24
26
|
export const useSessionStore = createWithEqualityFn<SessionStore>()(
|
|
25
27
|
subscribeWithSelector(
|
package/src/store/tool/store.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { devtools } from 'zustand/middleware';
|
|
2
1
|
import { shallow } from 'zustand/shallow';
|
|
3
2
|
import { createWithEqualityFn } from 'zustand/traditional';
|
|
4
3
|
import { StateCreator } from 'zustand/vanilla';
|
|
5
4
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
5
|
+
import { createDevtools } from '../middleware/createDevtools';
|
|
8
6
|
import { ToolStoreState, initialState } from './initialState';
|
|
9
7
|
import { BuiltinToolAction, createBuiltinToolSlice } from './slices/builtin';
|
|
10
8
|
import { CustomPluginAction, createCustomPluginSlice } from './slices/customPlugin';
|
|
@@ -29,9 +27,6 @@ const createStore: StateCreator<ToolStore, [['zustand/devtools', never]]> = (...
|
|
|
29
27
|
|
|
30
28
|
// =============== 实装 useStore ============ //
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}),
|
|
36
|
-
shallow,
|
|
37
|
-
);
|
|
30
|
+
const devtools = createDevtools('tools');
|
|
31
|
+
|
|
32
|
+
export const useToolStore = createWithEqualityFn<ToolStore>()(devtools(createStore), shallow);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { userGeneralSettingsSelectors } from './slices/settings/selectors';
|
|
2
2
|
import { useUserStore } from './store';
|
|
3
3
|
|
|
4
|
-
const getCurrentLanguage = () =>
|
|
4
|
+
const getCurrentLanguage = () =>
|
|
5
|
+
userGeneralSettingsSelectors.currentLanguage(useUserStore.getState());
|
|
5
6
|
|
|
6
7
|
export const globalHelpers = {
|
|
7
8
|
getCurrentLanguage,
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
export { authSelectors, userProfileSelectors } from './slices/auth/selectors';
|
|
2
|
-
export {
|
|
2
|
+
export {
|
|
3
|
+
keyVaultsConfigSelectors,
|
|
4
|
+
modelConfigSelectors,
|
|
5
|
+
modelProviderSelectors,
|
|
6
|
+
} from './slices/modelList/selectors';
|
|
3
7
|
export { preferenceSelectors } from './slices/preference/selectors';
|
|
4
|
-
export {
|
|
8
|
+
export {
|
|
9
|
+
settingsSelectors,
|
|
10
|
+
systemAgentSelectors,
|
|
11
|
+
userGeneralSettingsSelectors,
|
|
12
|
+
} from './slices/settings/selectors';
|
|
5
13
|
export { syncSettingsSelectors } from './slices/sync/selectors';
|
|
@@ -84,7 +84,7 @@ describe('createCommonSlice', () => {
|
|
|
84
84
|
telemetry: true,
|
|
85
85
|
},
|
|
86
86
|
settings: {
|
|
87
|
-
language: 'en-US',
|
|
87
|
+
general: { language: 'en-US' },
|
|
88
88
|
},
|
|
89
89
|
};
|
|
90
90
|
|
|
@@ -166,7 +166,7 @@ describe('createCommonSlice', () => {
|
|
|
166
166
|
isOnboard: true,
|
|
167
167
|
preference: savedPreference,
|
|
168
168
|
settings: {
|
|
169
|
-
language: 'en-US',
|
|
169
|
+
general: { language: 'en-US' },
|
|
170
170
|
},
|
|
171
171
|
};
|
|
172
172
|
vi.spyOn(userService, 'getUserState').mockResolvedValueOnce(mockUserState);
|
|
@@ -216,7 +216,7 @@ describe('createCommonSlice', () => {
|
|
|
216
216
|
isOnboard: true,
|
|
217
217
|
preference: {} as any,
|
|
218
218
|
settings: {
|
|
219
|
-
language: 'en-US',
|
|
219
|
+
general: { language: 'en-US' },
|
|
220
220
|
},
|
|
221
221
|
};
|
|
222
222
|
|
|
@@ -7,14 +7,14 @@ import { userService } from '@/services/user';
|
|
|
7
7
|
import { ClientService } from '@/services/user/client';
|
|
8
8
|
import type { UserStore } from '@/store/user';
|
|
9
9
|
import type { GlobalServerConfig } from '@/types/serverConfig';
|
|
10
|
-
import type { GlobalSettings } from '@/types/settings';
|
|
11
10
|
import { UserInitializationState } from '@/types/user';
|
|
11
|
+
import type { UserSettings } from '@/types/user/settings';
|
|
12
12
|
import { switchLang } from '@/utils/client/switchLang';
|
|
13
13
|
import { merge } from '@/utils/merge';
|
|
14
14
|
import { setNamespace } from '@/utils/storeDebug';
|
|
15
15
|
|
|
16
16
|
import { preferenceSelectors } from '../preference/selectors';
|
|
17
|
-
import {
|
|
17
|
+
import { userGeneralSettingsSelectors } from '../settings/selectors';
|
|
18
18
|
|
|
19
19
|
const n = setNamespace('common');
|
|
20
20
|
|
|
@@ -78,7 +78,7 @@ export const createCommonSlice: StateCreator<
|
|
|
78
78
|
|
|
79
79
|
if (data) {
|
|
80
80
|
// merge settings
|
|
81
|
-
const serverSettings: DeepPartial<
|
|
81
|
+
const serverSettings: DeepPartial<UserSettings> = {
|
|
82
82
|
defaultAgent: serverConfig.defaultAgent,
|
|
83
83
|
languageModel: serverConfig.languageModel,
|
|
84
84
|
};
|
|
@@ -115,7 +115,7 @@ export const createCommonSlice: StateCreator<
|
|
|
115
115
|
get().refreshDefaultModelProviderList({ trigger: 'fetchUserState' });
|
|
116
116
|
|
|
117
117
|
// auto switch language
|
|
118
|
-
const
|
|
118
|
+
const language = userGeneralSettingsSelectors.config(get()).language;
|
|
119
119
|
if (language === 'auto') {
|
|
120
120
|
switchLang('auto');
|
|
121
121
|
}
|
|
@@ -4,7 +4,7 @@ import { describe, expect, it, vi } from 'vitest';
|
|
|
4
4
|
import { modelsService } from '@/services/models';
|
|
5
5
|
import { userService } from '@/services/user';
|
|
6
6
|
import { useUserStore } from '@/store/user';
|
|
7
|
-
import {
|
|
7
|
+
import { ProviderConfig } from '@/types/user/settings';
|
|
8
8
|
|
|
9
9
|
import { settingsSelectors } from '../settings/selectors';
|
|
10
10
|
import { CustomModelCardDispatch } from './reducers/customModelCard';
|
|
@@ -24,7 +24,7 @@ describe('LLMSettingsSliceAction', () => {
|
|
|
24
24
|
describe('setModelProviderConfig', () => {
|
|
25
25
|
it('should set OpenAI configuration', async () => {
|
|
26
26
|
const { result } = renderHook(() => useUserStore());
|
|
27
|
-
const openAIConfig: Partial<
|
|
27
|
+
const openAIConfig: Partial<ProviderConfig> = { fetchOnClient: true };
|
|
28
28
|
|
|
29
29
|
// Perform the action
|
|
30
30
|
await act(async () => {
|
|
@@ -6,7 +6,11 @@ import { DEFAULT_MODEL_PROVIDER_LIST } from '@/config/modelProviders';
|
|
|
6
6
|
import { ModelProvider } from '@/libs/agent-runtime';
|
|
7
7
|
import { UserStore } from '@/store/user';
|
|
8
8
|
import { ChatModelCard } from '@/types/llm';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
GlobalLLMProviderKey,
|
|
11
|
+
UserKeyVaults,
|
|
12
|
+
UserModelProviderConfig,
|
|
13
|
+
} from '@/types/user/settings';
|
|
10
14
|
|
|
11
15
|
import { settingsSelectors } from '../settings/selectors';
|
|
12
16
|
import { CustomModelCardDispatch, customModelCardsReducer } from './reducers/customModelCard';
|
|
@@ -28,9 +32,8 @@ export interface ModelListAction {
|
|
|
28
32
|
removeEnabledModels: (provider: GlobalLLMProviderKey, model: string) => Promise<void>;
|
|
29
33
|
setModelProviderConfig: <T extends GlobalLLMProviderKey>(
|
|
30
34
|
provider: T,
|
|
31
|
-
config: Partial<
|
|
35
|
+
config: Partial<UserModelProviderConfig[T]>,
|
|
32
36
|
) => Promise<void>;
|
|
33
|
-
|
|
34
37
|
toggleEditingCustomModelCard: (params?: { id: string; provider: GlobalLLMProviderKey }) => void;
|
|
35
38
|
|
|
36
39
|
toggleProviderEnabled: (provider: GlobalLLMProviderKey, enabled: boolean) => Promise<void>;
|
|
@@ -41,6 +44,11 @@ export interface ModelListAction {
|
|
|
41
44
|
options: { label?: string; value?: string }[],
|
|
42
45
|
) => Promise<void>;
|
|
43
46
|
|
|
47
|
+
updateKeyVaultConfig: <T extends GlobalLLMProviderKey>(
|
|
48
|
+
provider: T,
|
|
49
|
+
config: Partial<UserKeyVaults[T]>,
|
|
50
|
+
) => Promise<void>;
|
|
51
|
+
|
|
44
52
|
useFetchProviderModelList: (
|
|
45
53
|
provider: GlobalLLMProviderKey,
|
|
46
54
|
enabledAutoFetch: boolean,
|
|
@@ -137,10 +145,10 @@ export const createModelListSlice: StateCreator<
|
|
|
137
145
|
toggleEditingCustomModelCard: (params) => {
|
|
138
146
|
set({ editingCustomCardModel: params }, false, 'toggleEditingCustomModelCard');
|
|
139
147
|
},
|
|
148
|
+
|
|
140
149
|
toggleProviderEnabled: async (provider, enabled) => {
|
|
141
150
|
await get().setSettings({ languageModel: { [provider]: { enabled } } });
|
|
142
151
|
},
|
|
143
|
-
|
|
144
152
|
updateEnabledModels: async (provider, value, options) => {
|
|
145
153
|
const { dispatchCustomModelCards, setModelProviderConfig } = get();
|
|
146
154
|
const enabledModels = modelProviderSelectors.getEnableModelsById(provider)(get());
|
|
@@ -171,6 +179,10 @@ export const createModelListSlice: StateCreator<
|
|
|
171
179
|
await setModelProviderConfig(provider, { enabledModels: value.filter(Boolean) });
|
|
172
180
|
},
|
|
173
181
|
|
|
182
|
+
updateKeyVaultConfig: async (provider, config) => {
|
|
183
|
+
await get().setSettings({ keyVaults: { [provider]: config } });
|
|
184
|
+
},
|
|
185
|
+
|
|
174
186
|
useFetchProviderModelList: (provider, enabledAutoFetch) =>
|
|
175
187
|
useSWR<ChatModelCard[] | undefined>(
|
|
176
188
|
[provider, enabledAutoFetch],
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { UserStore } from '@/store/user';
|
|
2
|
+
import { GlobalLLMProviderKey, OpenAICompatibleKeyVault } from '@/types/user/settings';
|
|
3
|
+
|
|
4
|
+
import { currentSettings } from '../../settings/selectors/settings';
|
|
5
|
+
|
|
6
|
+
export const keyVaultsSettings = (s: UserStore) => currentSettings(s).keyVaults;
|
|
7
|
+
|
|
8
|
+
const openAIConfig = (s: UserStore) => keyVaultsSettings(s).openai || {};
|
|
9
|
+
const bedrockConfig = (s: UserStore) => keyVaultsSettings(s).bedrock || {};
|
|
10
|
+
const ollamaConfig = (s: UserStore) => keyVaultsSettings(s).ollama || {};
|
|
11
|
+
const azureConfig = (s: UserStore) => keyVaultsSettings(s).azure || {};
|
|
12
|
+
const getVaultByProvider = (provider: GlobalLLMProviderKey) => (s: UserStore) =>
|
|
13
|
+
(keyVaultsSettings(s)[provider] || {}) as OpenAICompatibleKeyVault;
|
|
14
|
+
|
|
15
|
+
const isProviderEndpointNotEmpty = (provider: string) => (s: UserStore) =>
|
|
16
|
+
!!getVaultByProvider(provider as GlobalLLMProviderKey)(s)?.baseURL;
|
|
17
|
+
|
|
18
|
+
export const keyVaultsConfigSelectors = {
|
|
19
|
+
azureConfig,
|
|
20
|
+
bedrockConfig,
|
|
21
|
+
getVaultByProvider,
|
|
22
|
+
isProviderEndpointNotEmpty,
|
|
23
|
+
ollamaConfig,
|
|
24
|
+
openAIConfig,
|
|
25
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GlobalLLMProviderKey } from '@/types/settings';
|
|
1
|
+
import { GlobalLLMProviderKey } from '@/types/user/settings';
|
|
2
2
|
|
|
3
3
|
import { UserStore } from '../../../store';
|
|
4
4
|
import { currentLLMSettings, getProviderConfigById } from '../../settings/selectors/settings';
|
|
@@ -6,9 +6,6 @@ import { currentLLMSettings, getProviderConfigById } from '../../settings/select
|
|
|
6
6
|
const isProviderEnabled = (provider: GlobalLLMProviderKey) => (s: UserStore) =>
|
|
7
7
|
getProviderConfigById(provider)(s)?.enabled || false;
|
|
8
8
|
|
|
9
|
-
const isProviderEndpointNotEmpty = (provider: GlobalLLMProviderKey | string) => (s: UserStore) =>
|
|
10
|
-
!!getProviderConfigById(provider)(s)?.endpoint;
|
|
11
|
-
|
|
12
9
|
const isProviderFetchOnClient = (provider: GlobalLLMProviderKey | string) => (s: UserStore) => {
|
|
13
10
|
const config = getProviderConfigById(provider)(s);
|
|
14
11
|
if (typeof config?.fetchOnClient !== 'undefined') return config?.fetchOnClient;
|
|
@@ -56,10 +53,8 @@ export const modelConfigSelectors = {
|
|
|
56
53
|
isAutoFetchModelsEnabled,
|
|
57
54
|
isAzureEnabled,
|
|
58
55
|
isProviderEnabled,
|
|
59
|
-
isProviderEndpointNotEmpty,
|
|
60
56
|
isProviderFetchOnClient,
|
|
61
57
|
|
|
62
58
|
ollamaConfig,
|
|
63
|
-
|
|
64
59
|
openAIConfig,
|
|
65
60
|
};
|
|
@@ -3,7 +3,7 @@ import { uniqBy } from 'lodash-es';
|
|
|
3
3
|
import { filterEnabledModels } from '@/config/modelProviders';
|
|
4
4
|
import { ChatModelCard, ModelProviderCard } from '@/types/llm';
|
|
5
5
|
import { ServerModelProviderConfig } from '@/types/serverConfig';
|
|
6
|
-
import { GlobalLLMProviderKey } from '@/types/settings';
|
|
6
|
+
import { GlobalLLMProviderKey } from '@/types/user/settings';
|
|
7
7
|
|
|
8
8
|
import { UserStore } from '../../../store';
|
|
9
9
|
import { currentSettings, getProviderConfigById } from '../../settings/selectors/settings';
|
|
@@ -7,7 +7,10 @@ import { DEFAULT_AGENT, DEFAULT_SETTINGS } from '@/const/settings';
|
|
|
7
7
|
import { userService } from '@/services/user';
|
|
8
8
|
import { useUserStore } from '@/store/user';
|
|
9
9
|
import { LobeAgentSettings } from '@/types/session';
|
|
10
|
-
import {
|
|
10
|
+
import { UserSettings } from '@/types/user/settings';
|
|
11
|
+
import { merge } from '@/utils/merge';
|
|
12
|
+
|
|
13
|
+
vi.mock('zustand/traditional');
|
|
11
14
|
|
|
12
15
|
// Mock userService
|
|
13
16
|
vi.mock('@/services/user', () => ({
|
|
@@ -21,10 +24,9 @@ describe('SettingsAction', () => {
|
|
|
21
24
|
describe('importAppSettings', () => {
|
|
22
25
|
it('should import app settings', async () => {
|
|
23
26
|
const { result } = renderHook(() => useUserStore());
|
|
24
|
-
const newSettings:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
};
|
|
27
|
+
const newSettings: UserSettings = merge(DEFAULT_SETTINGS, {
|
|
28
|
+
general: { themeMode: 'dark' },
|
|
29
|
+
});
|
|
28
30
|
|
|
29
31
|
// Mock the internal setSettings function call
|
|
30
32
|
const setSettingsSpy = vi.spyOn(result.current, 'setSettings');
|
|
@@ -35,14 +37,12 @@ describe('SettingsAction', () => {
|
|
|
35
37
|
});
|
|
36
38
|
|
|
37
39
|
// Assert that setSettings was called with the correct settings
|
|
38
|
-
expect(setSettingsSpy).toHaveBeenCalledWith(
|
|
39
|
-
...DEFAULT_SETTINGS,
|
|
40
|
-
password: undefined,
|
|
41
|
-
themeMode: 'dark',
|
|
42
|
-
});
|
|
40
|
+
expect(setSettingsSpy).toHaveBeenCalledWith(newSettings);
|
|
43
41
|
|
|
44
42
|
// Assert that the state has been updated
|
|
45
|
-
expect(userService.updateUserSettings).toHaveBeenCalledWith({
|
|
43
|
+
expect(userService.updateUserSettings).toHaveBeenCalledWith({
|
|
44
|
+
general: { themeMode: 'dark' },
|
|
45
|
+
});
|
|
46
46
|
|
|
47
47
|
// Restore the spy
|
|
48
48
|
setSettingsSpy.mockRestore();
|
|
@@ -69,7 +69,7 @@ describe('SettingsAction', () => {
|
|
|
69
69
|
describe('setSettings', () => {
|
|
70
70
|
it('should set partial settings', async () => {
|
|
71
71
|
const { result } = renderHook(() => useUserStore());
|
|
72
|
-
const partialSettings:
|
|
72
|
+
const partialSettings: DeepPartial<UserSettings> = { general: { themeMode: 'dark' } };
|
|
73
73
|
|
|
74
74
|
// Perform the action
|
|
75
75
|
await act(async () => {
|
|
@@ -92,7 +92,9 @@ describe('SettingsAction', () => {
|
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
// Assert that updateUserSettings was called with the correct theme mode
|
|
95
|
-
expect(userService.updateUserSettings).toHaveBeenCalledWith({
|
|
95
|
+
expect(userService.updateUserSettings).toHaveBeenCalledWith({
|
|
96
|
+
general: { themeMode },
|
|
97
|
+
});
|
|
96
98
|
});
|
|
97
99
|
});
|
|
98
100
|
|
|
@@ -116,7 +118,7 @@ describe('SettingsAction', () => {
|
|
|
116
118
|
describe('setTranslationSystemAgent', () => {
|
|
117
119
|
it('should set partial settings', async () => {
|
|
118
120
|
const { result } = renderHook(() => useUserStore());
|
|
119
|
-
const systemAgentSettings: Partial<
|
|
121
|
+
const systemAgentSettings: Partial<UserSettings> = {
|
|
120
122
|
systemAgent: {
|
|
121
123
|
translation: {
|
|
122
124
|
model: 'testmodel',
|
|
@@ -7,19 +7,21 @@ import { userService } from '@/services/user';
|
|
|
7
7
|
import type { UserStore } from '@/store/user';
|
|
8
8
|
import { LocaleMode } from '@/types/locale';
|
|
9
9
|
import { LobeAgentSettings } from '@/types/session';
|
|
10
|
-
import {
|
|
10
|
+
import { UserGeneralConfig, UserKeyVaults, UserSettings } from '@/types/user/settings';
|
|
11
11
|
import { switchLang } from '@/utils/client/switchLang';
|
|
12
12
|
import { difference } from '@/utils/difference';
|
|
13
13
|
import { merge } from '@/utils/merge';
|
|
14
14
|
|
|
15
15
|
export interface UserSettingsAction {
|
|
16
|
-
importAppSettings: (settings:
|
|
16
|
+
importAppSettings: (settings: UserSettings) => Promise<void>;
|
|
17
17
|
resetSettings: () => Promise<void>;
|
|
18
|
-
setSettings: (settings: DeepPartial<
|
|
18
|
+
setSettings: (settings: DeepPartial<UserSettings>) => Promise<void>;
|
|
19
19
|
setTranslationSystemAgent: (provider: string, model: string) => Promise<void>;
|
|
20
20
|
switchLocale: (locale: LocaleMode) => Promise<void>;
|
|
21
21
|
switchThemeMode: (themeMode: ThemeMode) => Promise<void>;
|
|
22
22
|
updateDefaultAgent: (agent: DeepPartial<LobeAgentSettings>) => Promise<void>;
|
|
23
|
+
updateGeneralConfig: (settings: Partial<UserGeneralConfig>) => Promise<void>;
|
|
24
|
+
updateKeyVaults: (settings: Partial<UserKeyVaults>) => Promise<void>;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export const createSettingsSlice: StateCreator<
|
|
@@ -30,10 +32,8 @@ export const createSettingsSlice: StateCreator<
|
|
|
30
32
|
> = (set, get) => ({
|
|
31
33
|
importAppSettings: async (importAppSettings) => {
|
|
32
34
|
const { setSettings } = get();
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
34
|
-
const { password: _, ...settings } = importAppSettings;
|
|
35
35
|
|
|
36
|
-
await setSettings(
|
|
36
|
+
await setSettings(importAppSettings);
|
|
37
37
|
},
|
|
38
38
|
resetSettings: async () => {
|
|
39
39
|
await userService.resetUserSettings();
|
|
@@ -62,14 +62,20 @@ export const createSettingsSlice: StateCreator<
|
|
|
62
62
|
});
|
|
63
63
|
},
|
|
64
64
|
switchLocale: async (locale) => {
|
|
65
|
-
await get().
|
|
65
|
+
await get().updateGeneralConfig({ language: locale });
|
|
66
66
|
|
|
67
67
|
switchLang(locale);
|
|
68
68
|
},
|
|
69
69
|
switchThemeMode: async (themeMode) => {
|
|
70
|
-
await get().
|
|
70
|
+
await get().updateGeneralConfig({ themeMode });
|
|
71
71
|
},
|
|
72
72
|
updateDefaultAgent: async (defaultAgent) => {
|
|
73
73
|
await get().setSettings({ defaultAgent });
|
|
74
74
|
},
|
|
75
|
+
updateGeneralConfig: async (general) => {
|
|
76
|
+
await get().setSettings({ general });
|
|
77
|
+
},
|
|
78
|
+
updateKeyVaults: async (keyVaults) => {
|
|
79
|
+
await get().setSettings({ keyVaults });
|
|
80
|
+
},
|
|
75
81
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { DeepPartial } from 'utility-types';
|
|
2
2
|
|
|
3
3
|
import { DEFAULT_SETTINGS } from '@/const/settings';
|
|
4
|
-
import {
|
|
4
|
+
import { UserSettings } from '@/types/user/settings';
|
|
5
5
|
|
|
6
6
|
export interface UserSettingsState {
|
|
7
|
-
defaultSettings:
|
|
8
|
-
settings: DeepPartial<
|
|
7
|
+
defaultSettings: UserSettings;
|
|
8
|
+
settings: DeepPartial<UserSettings>;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export const initialSettingsState: UserSettingsState = {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { UserStore } from '@/store/user';
|
|
2
|
+
import { UserState, initialState } from '@/store/user/initialState';
|
|
3
|
+
import { merge } from '@/utils/merge';
|
|
4
|
+
|
|
5
|
+
import { userGeneralSettingsSelectors } from './general';
|
|
6
|
+
|
|
7
|
+
describe('settingsSelectors', () => {
|
|
8
|
+
describe('currentLanguage', () => {
|
|
9
|
+
it('should return the correct language setting', () => {
|
|
10
|
+
const s: UserState = merge(initialState, {
|
|
11
|
+
settings: {
|
|
12
|
+
general: { language: 'fr' },
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const result = userGeneralSettingsSelectors.currentLanguage(s as UserStore);
|
|
17
|
+
|
|
18
|
+
expect(result).toBe('fr');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('currentThemeMode', () => {
|
|
23
|
+
it('should return the correct theme', () => {
|
|
24
|
+
const s: UserState = merge(initialState, {
|
|
25
|
+
settings: {
|
|
26
|
+
general: { themeMode: 'light' },
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const result = userGeneralSettingsSelectors.currentThemeMode(s as UserStore);
|
|
31
|
+
|
|
32
|
+
expect(result).toBe('light');
|
|
33
|
+
});
|
|
34
|
+
it('should return the auto if not set the themeMode', () => {
|
|
35
|
+
const s: UserState = merge(initialState, {
|
|
36
|
+
settings: {
|
|
37
|
+
general: { themeMode: undefined },
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
const result = userGeneralSettingsSelectors.currentThemeMode(s as UserStore);
|
|
41
|
+
|
|
42
|
+
expect(result).toBe('auto');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DEFAULT_LANG } from '@/const/locale';
|
|
2
|
+
import { Locales } from '@/locales/resources';
|
|
3
|
+
import { isOnServerSide } from '@/utils/env';
|
|
4
|
+
|
|
5
|
+
import { UserStore } from '../../../store';
|
|
6
|
+
import { currentSettings } from './settings';
|
|
7
|
+
|
|
8
|
+
const generalConfig = (s: UserStore) => currentSettings(s).general || {};
|
|
9
|
+
|
|
10
|
+
const currentLanguage = (s: UserStore) => {
|
|
11
|
+
const locale = generalConfig(s).language;
|
|
12
|
+
|
|
13
|
+
if (locale === 'auto') {
|
|
14
|
+
if (isOnServerSide) return DEFAULT_LANG;
|
|
15
|
+
|
|
16
|
+
return navigator.language as Locales;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return locale;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const currentThemeMode = (s: UserStore) => {
|
|
23
|
+
const themeMode = generalConfig(s).themeMode;
|
|
24
|
+
return themeMode || 'auto';
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const neutralColor = (s: UserStore) => generalConfig(s).neutralColor;
|
|
28
|
+
const primaryColor = (s: UserStore) => generalConfig(s).primaryColor;
|
|
29
|
+
const fontSize = (s: UserStore) => generalConfig(s).fontSize;
|
|
30
|
+
const language = (s: UserStore) => generalConfig(s).language;
|
|
31
|
+
|
|
32
|
+
export const userGeneralSettingsSelectors = {
|
|
33
|
+
config: generalConfig,
|
|
34
|
+
currentLanguage,
|
|
35
|
+
currentThemeMode,
|
|
36
|
+
fontSize,
|
|
37
|
+
language,
|
|
38
|
+
neutralColor,
|
|
39
|
+
primaryColor,
|
|
40
|
+
};
|