@lobehub/chat 0.122.2 → 0.122.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +8 -8
  3. package/README.zh-CN.md +8 -8
  4. package/docs/Deployment/Docker-Deployment.md +118 -0
  5. package/docs/Deployment/Docker-Deployment.zh-CN.md +118 -0
  6. package/package.json +1 -4
  7. package/src/app/api/config/route.ts +3 -1
  8. package/src/app/api/openai/chat/createChatCompletion.ts +1 -1
  9. package/src/app/api/openai/createBizOpenAI/index.ts +1 -1
  10. package/src/app/api/plugin/gateway/route.ts +31 -5
  11. package/src/app/chat/(mobile)/features/SessionHeader.tsx +2 -1
  12. package/src/app/chat/features/ChatHeader/SettingButton.tsx +1 -1
  13. package/src/app/chat/features/ChatHeader/ShareButton/ShareModal.tsx +2 -1
  14. package/src/app/chat/settings/(desktop)/index.tsx +9 -5
  15. package/src/app/chat/settings/features/SubmitAgentButton/SubmitAgentModal.tsx +2 -1
  16. package/src/app/settings/(mobile)/mobile/index.tsx +2 -1
  17. package/src/app/settings/common/Common.tsx +14 -3
  18. package/src/app/settings/features/ThemeSwatches/ThemeSwatchesNeutral.tsx +2 -1
  19. package/src/app/settings/features/ThemeSwatches/ThemeSwatchesPrimary.tsx +2 -1
  20. package/src/app/settings/llm/LLM/index.tsx +17 -10
  21. package/src/const/settings.ts +0 -1
  22. package/src/database/core/db.ts +41 -2
  23. package/src/database/core/migrations/migrateSettingsToUser/fixtures/input.json +55 -0
  24. package/src/database/core/migrations/migrateSettingsToUser/fixtures/output.json +60 -0
  25. package/src/database/core/migrations/migrateSettingsToUser/index.test.ts +13 -0
  26. package/src/database/core/migrations/migrateSettingsToUser/index.ts +39 -0
  27. package/src/database/core/schemas.ts +10 -0
  28. package/src/database/models/user.ts +54 -0
  29. package/src/database/schemas/user.ts +57 -0
  30. package/src/features/AgentSetting/AgentConfig/index.tsx +2 -2
  31. package/src/features/AvatarWithUpload/index.tsx +6 -2
  32. package/src/features/ChatInput/ActionBar/ModelSwitch.tsx +2 -2
  33. package/src/features/Conversation/Error/ApiKeyForm.tsx +3 -3
  34. package/src/features/Conversation/Error/InvalidAccess.tsx +5 -1
  35. package/src/features/Conversation/Extras/Translate.tsx +3 -4
  36. package/src/layout/GlobalLayout/AppTheme.tsx +4 -3
  37. package/src/layout/GlobalLayout/Locale.tsx +2 -1
  38. package/src/layout/GlobalLayout/StoreHydration.tsx +7 -2
  39. package/src/services/_header.ts +3 -2
  40. package/src/services/chat.ts +2 -1
  41. package/src/services/user.ts +31 -0
  42. package/src/store/chat/slices/message/selectors.test.ts +7 -2
  43. package/src/store/chat/slices/message/selectors.ts +2 -1
  44. package/src/store/global/hooks/index.ts +0 -1
  45. package/src/store/global/initialState.ts +3 -1
  46. package/src/store/global/selectors.ts +1 -0
  47. package/src/store/global/slices/common/action.test.ts +2 -79
  48. package/src/store/global/slices/common/action.ts +37 -61
  49. package/src/store/global/slices/common/initialState.ts +0 -34
  50. package/src/store/global/slices/common/selectors.ts +2 -7
  51. package/src/store/global/slices/preference/action.test.ts +92 -0
  52. package/src/store/global/slices/preference/action.ts +75 -0
  53. package/src/store/global/slices/preference/initialState.ts +45 -0
  54. package/src/store/global/slices/preference/selectors.ts +10 -0
  55. package/src/store/global/slices/settings/action.test.ts +68 -39
  56. package/src/store/global/slices/settings/action.ts +26 -50
  57. package/src/store/global/slices/settings/initialState.ts +8 -6
  58. package/src/store/global/slices/settings/selectors/__snapshots__/modelProvider.test.ts.snap +100 -0
  59. package/src/store/global/slices/settings/{__snapshots__ → selectors/__snapshots__}/selectors.test.ts.snap +1 -115
  60. package/src/store/global/slices/settings/selectors/index.ts +2 -0
  61. package/src/store/global/slices/settings/selectors/modelProvider.test.ts +100 -0
  62. package/src/store/global/slices/settings/selectors/modelProvider.ts +64 -0
  63. package/src/store/global/slices/settings/{selectors.test.ts → selectors/selectors.test.ts} +2 -97
  64. package/src/store/global/slices/settings/selectors/settings.ts +58 -0
  65. package/src/store/global/store.ts +11 -36
  66. package/src/store/session/slices/agent/action.ts +1 -1
  67. package/src/types/settings/base.ts +13 -0
  68. package/src/types/settings/index.ts +34 -0
  69. package/src/types/settings/modelProvider.ts +17 -0
  70. package/src/types/settings/tts.ts +10 -0
  71. package/src/utils/difference.ts +12 -0
  72. package/tests/utils.tsx +8 -0
  73. package/tsconfig.json +9 -8
  74. package/vitest.config.ts +1 -0
  75. package/src/store/global/hooks/useHydrated.ts +0 -18
  76. package/src/store/global/slices/settings/selectors.ts +0 -105
  77. package/src/types/settings.ts +0 -68
  78. /package/src/app/api/{openai/errorResponse.test.ts → errorResponse.test.ts} +0 -0
  79. /package/src/app/api/{openai/errorResponse.ts → errorResponse.ts} +0 -0
@@ -0,0 +1,64 @@
1
+ import { DEFAULT_OPENAI_MODEL_LIST } from '@/const/llm';
2
+ import { CustomModels } from '@/types/settings';
3
+
4
+ import { GlobalStore } from '../../../store';
5
+ import { currentSettings } from './settings';
6
+
7
+ const openAIConfig = (s: GlobalStore) => currentSettings(s).languageModel.openAI;
8
+
9
+ const openAIAPIKeySelectors = (s: GlobalStore) =>
10
+ currentSettings(s).languageModel.openAI.OPENAI_API_KEY;
11
+
12
+ const enableAzure = (s: GlobalStore) => currentSettings(s).languageModel.openAI.useAzure;
13
+
14
+ const openAIProxyUrlSelectors = (s: GlobalStore) =>
15
+ currentSettings(s).languageModel.openAI.endpoint;
16
+
17
+ const modelListSelectors = (s: GlobalStore) => {
18
+ let models: CustomModels = [];
19
+
20
+ const removedModels: string[] = [];
21
+ const modelNames = [
22
+ ...DEFAULT_OPENAI_MODEL_LIST,
23
+ ...(s.serverConfig.customModelName || '').split(/[,,]/).filter(Boolean),
24
+ ...(currentSettings(s).languageModel.openAI.customModelName || '')
25
+ .split(/[,,]/)
26
+ .filter(Boolean),
27
+ ];
28
+
29
+ for (const item of modelNames) {
30
+ const disable = item.startsWith('-');
31
+ const nameConfig = item.startsWith('+') || item.startsWith('-') ? item.slice(1) : item;
32
+ const [name, displayName] = nameConfig.split('=');
33
+
34
+ if (disable) {
35
+ // Disable all models.
36
+ if (name === 'all') {
37
+ models = [];
38
+ }
39
+ removedModels.push(name);
40
+ continue;
41
+ }
42
+
43
+ // Remove duplicate model entries.
44
+ const existingIndex = models.findIndex(({ name: n }) => n === name);
45
+ if (existingIndex !== -1) {
46
+ models.splice(existingIndex, 1);
47
+ }
48
+
49
+ models.push({
50
+ displayName: displayName || name,
51
+ name,
52
+ });
53
+ }
54
+
55
+ return models.filter((m) => !removedModels.includes(m.name));
56
+ };
57
+
58
+ export const modelProviderSelectors = {
59
+ enableAzure,
60
+ modelList: modelListSelectors,
61
+ openAIAPI: openAIAPIKeySelectors,
62
+ openAIConfig,
63
+ openAIProxyUrl: openAIProxyUrlSelectors,
64
+ };
@@ -1,7 +1,7 @@
1
1
  import { LanguageModel } from '@/types/llm';
2
2
 
3
- import { GlobalStore } from '../../store';
4
- import { settingsSelectors } from './selectors';
3
+ import { GlobalStore } from '../../../store';
4
+ import { settingsSelectors } from './settings';
5
5
 
6
6
  describe('settingsSelectors', () => {
7
7
  describe('currentSettings', () => {
@@ -58,101 +58,6 @@ describe('settingsSelectors', () => {
58
58
  });
59
59
  });
60
60
 
61
- describe('CUSTOM_MODELS', () => {
62
- it('custom deletion, addition, and renaming of models', () => {
63
- const s = {
64
- serverConfig: {
65
- customModelName:
66
- '-all,+llama,+claude-2,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k',
67
- },
68
- settings: {
69
- languageModel: {
70
- openAI: {},
71
- },
72
- },
73
- } as unknown as GlobalStore;
74
-
75
- const result = settingsSelectors.modelList(s);
76
-
77
- expect(result).toMatchSnapshot();
78
- });
79
-
80
- it('duplicate naming model', () => {
81
- const s = {
82
- serverConfig: {},
83
- settings: {
84
- languageModel: {
85
- openAI: {
86
- customModelName: 'gpt-4-1106-preview=gpt-4-turbo,gpt-4-1106-preview=gpt-4-32k',
87
- },
88
- },
89
- },
90
- } as unknown as GlobalStore;
91
-
92
- const result = settingsSelectors.modelList(s);
93
-
94
- expect(result).toMatchSnapshot();
95
- });
96
-
97
- it('should delete model', () => {
98
- const s = {
99
- serverConfig: {
100
- customModelName: '-gpt-4',
101
- },
102
- settings: {
103
- languageModel: {
104
- openAI: {},
105
- },
106
- },
107
- } as unknown as GlobalStore;
108
-
109
- const result = settingsSelectors.modelList(s);
110
-
111
- expect(result).toEqual([
112
- {
113
- displayName: 'gpt-3.5-turbo',
114
- name: 'gpt-3.5-turbo',
115
- },
116
- {
117
- displayName: 'gpt-3.5-turbo-1106',
118
- name: 'gpt-3.5-turbo-1106',
119
- },
120
- {
121
- displayName: 'gpt-3.5-turbo-16k',
122
- name: 'gpt-3.5-turbo-16k',
123
- },
124
- {
125
- displayName: 'gpt-4-32k',
126
- name: 'gpt-4-32k',
127
- },
128
- {
129
- displayName: 'gpt-4-1106-preview',
130
- name: 'gpt-4-1106-preview',
131
- },
132
- {
133
- displayName: 'gpt-4-vision-preview',
134
- name: 'gpt-4-vision-preview',
135
- },
136
- ]);
137
- });
138
-
139
- it('only add the model', () => {
140
- const s = {
141
- serverConfig: {},
142
- settings: {
143
- languageModel: {
144
- openAI: {
145
- customModelName: 'model1,model2,model3,model4',
146
- },
147
- },
148
- },
149
- } as unknown as GlobalStore;
150
-
151
- const result = settingsSelectors.modelList(s);
152
-
153
- expect(result).toMatchSnapshot();
154
- });
155
- });
156
61
  describe('defaultAgent', () => {
157
62
  it('should merge DEFAULT_AGENT and s.settings.defaultAgent correctly', () => {
158
63
  const s = {
@@ -0,0 +1,58 @@
1
+ import { DEFAULT_LANG } from '@/const/locale';
2
+ import { DEFAULT_AGENT_META } from '@/const/meta';
3
+ import { DEFAULT_AGENT, DEFAULT_AGENT_CONFIG, DEFAULT_TTS_CONFIG } from '@/const/settings';
4
+ import { Locales } from '@/locales/resources';
5
+ import { GlobalSettings } from '@/types/settings';
6
+ import { isOnServerSide } from '@/utils/env';
7
+ import { merge } from '@/utils/merge';
8
+
9
+ import { GlobalStore } from '../../../store';
10
+
11
+ export const currentSettings = (s: GlobalStore): GlobalSettings =>
12
+ merge(s.defaultSettings, s.settings);
13
+
14
+ const password = (s: GlobalStore) => currentSettings(s).password;
15
+
16
+ const currentTTS = (s: GlobalStore) => merge(DEFAULT_TTS_CONFIG, currentSettings(s).tts);
17
+
18
+ const defaultAgent = (s: GlobalStore) => merge(DEFAULT_AGENT, currentSettings(s).defaultAgent);
19
+
20
+ const defaultAgentConfig = (s: GlobalStore) => merge(DEFAULT_AGENT_CONFIG, defaultAgent(s).config);
21
+
22
+ const defaultAgentMeta = (s: GlobalStore) => merge(DEFAULT_AGENT_META, defaultAgent(s).meta);
23
+
24
+ // TODO: Maybe we can also export settings difference
25
+ const exportSettings = (s: GlobalStore) => {
26
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
27
+ const { password: _, ...settings } = currentSettings(s);
28
+
29
+ return settings as GlobalSettings;
30
+ };
31
+
32
+ const currentLanguage = (s: GlobalStore) => {
33
+ const locale = currentSettings(s).language;
34
+
35
+ if (locale === 'auto') {
36
+ if (isOnServerSide) return DEFAULT_LANG;
37
+
38
+ return navigator.language as Locales;
39
+ }
40
+
41
+ return locale;
42
+ };
43
+
44
+ const dalleConfig = (s: GlobalStore) => currentSettings(s).tool?.dalle || {};
45
+ const isDalleAutoGenerating = (s: GlobalStore) => currentSettings(s).tool?.dalle?.autoGenerate;
46
+
47
+ export const settingsSelectors = {
48
+ currentLanguage,
49
+ currentSettings,
50
+ currentTTS,
51
+ dalleConfig,
52
+ defaultAgent,
53
+ defaultAgentConfig,
54
+ defaultAgentMeta,
55
+ exportSettings,
56
+ isDalleAutoGenerating,
57
+ password,
58
+ };
@@ -1,66 +1,39 @@
1
- import { produce } from 'immer';
2
- import { PersistOptions, devtools, persist } from 'zustand/middleware';
1
+ import { PersistOptions, devtools, persist, subscribeWithSelector } from 'zustand/middleware';
3
2
  import { shallow } from 'zustand/shallow';
4
3
  import { createWithEqualityFn } from 'zustand/traditional';
5
4
  import { StateCreator } from 'zustand/vanilla';
6
5
 
7
- import { DEFAULT_AGENT, DEFAULT_LLM_CONFIG } from '@/const/settings';
8
- import { SessionDefaultGroup } from '@/types/session';
9
6
  import { isDev } from '@/utils/env';
10
7
 
11
8
  import { createHyperStorage } from '../middleware/createHyperStorage';
12
9
  import { type GlobalState, initialState } from './initialState';
13
10
  import { type CommonAction, createCommonSlice } from './slices/common/action';
11
+ import { type PreferenceAction, createPreferenceSlice } from './slices/preference/action';
14
12
  import { type SettingsAction, createSettingsSlice } from './slices/settings/action';
15
13
 
16
14
  // =============== 聚合 createStoreFn ============ //
17
15
 
18
- export type GlobalStore = CommonAction & GlobalState & SettingsAction;
16
+ export type GlobalStore = CommonAction & GlobalState & SettingsAction & PreferenceAction;
19
17
 
20
18
  const createStore: StateCreator<GlobalStore, [['zustand/devtools', never]]> = (...parameters) => ({
21
19
  ...initialState,
22
20
  ...createCommonSlice(...parameters),
23
21
  ...createSettingsSlice(...parameters),
22
+ ...createPreferenceSlice(...parameters),
24
23
  });
25
24
 
26
25
  // =============== persist 本地缓存中间件配置 ============ //
27
26
  type GlobalPersist = Pick<GlobalStore, 'preference' | 'settings'>;
28
27
 
29
28
  const persistOptions: PersistOptions<GlobalStore, GlobalPersist> = {
30
- merge: (persistedState, currentState) => {
31
- const state = persistedState as GlobalPersist;
32
-
33
- return {
34
- ...currentState,
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
- }),
42
- settings: produce(state.settings, (draft) => {
43
- if (!draft.defaultAgent) {
44
- draft.defaultAgent = DEFAULT_AGENT;
45
- }
46
-
47
- // migration to new data model
48
- if (!draft.languageModel) {
49
- draft.languageModel = {
50
- openAI: DEFAULT_LLM_CONFIG.openAI,
51
- };
52
- }
53
- }),
54
- };
55
- },
56
- name: 'LOBE_SETTINGS',
29
+ name: 'LOBE_GLOBAL',
57
30
 
58
31
  skipHydration: true,
59
32
 
60
33
  storage: createHyperStorage({
61
34
  localStorage: {
62
35
  dbName: 'LobeHub',
63
- selectors: ['preference', 'settings'],
36
+ selectors: ['preference'],
64
37
  },
65
38
  }),
66
39
  };
@@ -69,9 +42,11 @@ const persistOptions: PersistOptions<GlobalStore, GlobalPersist> = {
69
42
 
70
43
  export const useGlobalStore = createWithEqualityFn<GlobalStore>()(
71
44
  persist(
72
- devtools(createStore, {
73
- name: 'LobeChat_Global' + (isDev ? '_DEV' : ''),
74
- }),
45
+ subscribeWithSelector(
46
+ devtools(createStore, {
47
+ name: 'LobeChat_Global' + (isDev ? '_DEV' : ''),
48
+ }),
49
+ ),
75
50
  persistOptions,
76
51
  ),
77
52
  shallow,
@@ -58,7 +58,7 @@ export const createAgentSlice: StateCreator<
58
58
  // if is the inbox session, update the global config
59
59
  const isInbox = sessionSelectors.isInboxSession(get());
60
60
  if (isInbox) {
61
- useGlobalStore.getState().updateDefaultAgent({ config });
61
+ await useGlobalStore.getState().updateDefaultAgent({ config });
62
62
  } else {
63
63
  const session = sessionSelectors.currentSession(get());
64
64
  if (!session) return;
@@ -0,0 +1,13 @@
1
+ import type { NeutralColors, PrimaryColors } from '@lobehub/ui';
2
+ import type { ThemeMode } from 'antd-style';
3
+
4
+ import { LocaleMode } from '@/types/locale';
5
+
6
+ export interface GlobalBaseSettings {
7
+ fontSize: number;
8
+ language: LocaleMode;
9
+ neutralColor?: NeutralColors;
10
+ password: string;
11
+ primaryColor?: PrimaryColors;
12
+ themeMode: ThemeMode;
13
+ }
@@ -0,0 +1,34 @@
1
+ import { DeepPartial } from 'utility-types';
2
+
3
+ import type { LobeAgentSession } from '@/types/session';
4
+
5
+ import { GlobalBaseSettings } from './base';
6
+ import { GlobalLLMConfig } from './modelProvider';
7
+ import { GlobalTTSConfig } from './tts';
8
+
9
+ export type GlobalDefaultAgent = Pick<LobeAgentSession, 'config' | 'meta'>;
10
+
11
+ export * from './base';
12
+ export * from './modelProvider';
13
+ export * from './tts';
14
+
15
+ export interface GlobalTool {
16
+ dalle: {
17
+ autoGenerate: boolean;
18
+ };
19
+ }
20
+
21
+ export interface GlobalServerConfig {
22
+ customModelName?: string;
23
+ defaultAgent?: DeepPartial<GlobalDefaultAgent>;
24
+ }
25
+
26
+ /**
27
+ * 配置设置
28
+ */
29
+ export interface GlobalSettings extends GlobalBaseSettings {
30
+ defaultAgent: GlobalDefaultAgent;
31
+ languageModel: GlobalLLMConfig;
32
+ tool: GlobalTool;
33
+ tts: GlobalTTSConfig;
34
+ }
@@ -0,0 +1,17 @@
1
+ export type CustomModels = { displayName: string; name: string }[];
2
+
3
+ export interface OpenAIConfig {
4
+ OPENAI_API_KEY: string;
5
+ azureApiVersion?: string;
6
+ /**
7
+ * custom mode name for fine-tuning or openai like model
8
+ */
9
+ customModelName?: string;
10
+ endpoint?: string;
11
+ models?: string[];
12
+ useAzure?: boolean;
13
+ }
14
+
15
+ export interface GlobalLLMConfig {
16
+ openAI: OpenAIConfig;
17
+ }
@@ -0,0 +1,10 @@
1
+ export type STTServer = 'openai' | 'browser';
2
+
3
+ export interface GlobalTTSConfig {
4
+ openAI: {
5
+ sttModel: 'whisper-1';
6
+ ttsModel: 'tts-1' | 'tts-1-hd';
7
+ };
8
+ sttAutoStop: boolean;
9
+ sttServer: STTServer;
10
+ }
@@ -0,0 +1,12 @@
1
+ import { isEqual, isObject, transform } from 'lodash-es';
2
+
3
+ export const difference = <T extends object>(object: T, base: T) => {
4
+ const changes = (object: any, base: any) =>
5
+ transform(object, (result: any, value, key) => {
6
+ if (!isEqual(value, base[key])) {
7
+ result[key] = isObject(value) && isObject(base[key]) ? changes(value, base[key]) : value;
8
+ }
9
+ });
10
+
11
+ return changes(object, base);
12
+ };
@@ -0,0 +1,8 @@
1
+ import { SWRConfig } from 'swr';
2
+
3
+ // 全局的 SWR 配置
4
+ const swrConfig = {
5
+ provider: () => new Map(),
6
+ };
7
+
8
+ export const withSWR = ({ children }: any) => <SWRConfig value={swrConfig}>{children}</SWRConfig>;
package/tsconfig.json CHANGED
@@ -18,13 +18,14 @@
18
18
  "baseUrl": ".",
19
19
  "types": ["vitest/globals"],
20
20
  "paths": {
21
- "@/*": ["./src/*"]
21
+ "@/*": ["./src/*"],
22
+ "~test-utils": ["./tests/utils.tsx"],
22
23
  },
23
24
  "plugins": [
24
25
  {
25
- "name": "next"
26
- }
27
- ]
26
+ "name": "next",
27
+ },
28
+ ],
28
29
  },
29
30
  "exclude": ["node_modules"],
30
31
  "include": [
@@ -35,11 +36,11 @@
35
36
  "**/*.ts",
36
37
  "**/*.d.ts",
37
38
  "**/*.tsx",
38
- ".next/types/**/*.ts"
39
+ ".next/types/**/*.ts",
39
40
  ],
40
41
  "ts-node": {
41
42
  "compilerOptions": {
42
- "module": "commonjs"
43
- }
44
- }
43
+ "module": "commonjs",
44
+ },
45
+ },
45
46
  }
package/vitest.config.ts CHANGED
@@ -9,6 +9,7 @@ export default defineConfig({
9
9
  test: {
10
10
  alias: {
11
11
  '@': resolve(__dirname, './src'),
12
+ '~test-utils': resolve(__dirname, './tests/utils.tsx'),
12
13
  },
13
14
  coverage: {
14
15
  all: false,
@@ -1,18 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
-
3
- import { useGlobalStore } from '../store';
4
-
5
- export const useGlobalHydrated = () => {
6
- // 根据 sessions 是否有值来判断是否已经初始化
7
- const hasInited = !!Object.values(useGlobalStore.getState().settings).length;
8
-
9
- const [isInit, setInit] = useState(hasInited);
10
-
11
- useEffect(() => {
12
- useGlobalStore.persist.onFinishHydration(() => {
13
- if (!isInit) setInit(true);
14
- });
15
- }, []);
16
-
17
- return isInit;
18
- };
@@ -1,105 +0,0 @@
1
- import { DEFAULT_OPENAI_MODEL_LIST } from '@/const/llm';
2
- import { DEFAULT_LANG } from '@/const/locale';
3
- import { DEFAULT_AGENT_META } from '@/const/meta';
4
- import {
5
- DEFAULT_AGENT,
6
- DEFAULT_AGENT_CONFIG,
7
- DEFAULT_SETTINGS,
8
- DEFAULT_TTS_CONFIG,
9
- } from '@/const/settings';
10
- import { Locales } from '@/locales/resources';
11
- import { CustomModels, GlobalSettings } from '@/types/settings';
12
- import { isOnServerSide } from '@/utils/env';
13
- import { merge } from '@/utils/merge';
14
-
15
- import { GlobalStore } from '../../store';
16
-
17
- const currentSettings = (s: GlobalStore) => merge(DEFAULT_SETTINGS, s.settings);
18
-
19
- const currentTTS = (s: GlobalStore) => merge(DEFAULT_TTS_CONFIG, s.settings.tts);
20
-
21
- const defaultAgent = (s: GlobalStore) => merge(DEFAULT_AGENT, s.settings.defaultAgent);
22
-
23
- const defaultAgentConfig = (s: GlobalStore) => merge(DEFAULT_AGENT_CONFIG, defaultAgent(s).config);
24
-
25
- const defaultAgentMeta = (s: GlobalStore) => merge(DEFAULT_AGENT_META, defaultAgent(s).meta);
26
-
27
- const openAIAPIKeySelectors = (s: GlobalStore) => s.settings.languageModel.openAI.OPENAI_API_KEY;
28
-
29
- const openAIProxyUrlSelectors = (s: GlobalStore) => s.settings.languageModel.openAI.endpoint;
30
-
31
- const modelListSelectors = (s: GlobalStore) => {
32
- let models: CustomModels = [];
33
-
34
- const removedModels: string[] = [];
35
- const modelNames = [
36
- ...DEFAULT_OPENAI_MODEL_LIST,
37
- ...(s.serverConfig.customModelName || '').split(/[,,]/).filter(Boolean),
38
- ...(s.settings.languageModel.openAI.customModelName || '').split(/[,,]/).filter(Boolean),
39
- ];
40
-
41
- for (const item of modelNames) {
42
- const disable = item.startsWith('-');
43
- const nameConfig = item.startsWith('+') || item.startsWith('-') ? item.slice(1) : item;
44
- const [name, displayName] = nameConfig.split('=');
45
-
46
- if (disable) {
47
- // Disable all models.
48
- if (name === 'all') {
49
- models = [];
50
- }
51
- removedModels.push(name);
52
- continue;
53
- }
54
-
55
- // Remove duplicate model entries.
56
- const existingIndex = models.findIndex(({ name: n }) => n === name);
57
- if (existingIndex !== -1) {
58
- models.splice(existingIndex, 1);
59
- }
60
-
61
- models.push({
62
- displayName: displayName || name,
63
- name,
64
- });
65
- }
66
-
67
- return models.filter((m) => !removedModels.includes(m.name));
68
- };
69
-
70
- export const exportSettings = (s: GlobalStore) => {
71
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
72
- const { password: _, ...settings } = s.settings;
73
-
74
- return settings as GlobalSettings;
75
- };
76
-
77
- const currentLanguage = (s: GlobalStore) => {
78
- const locale = s.settings.language;
79
-
80
- if (locale === 'auto') {
81
- if (isOnServerSide) return DEFAULT_LANG;
82
-
83
- return navigator.language as Locales;
84
- }
85
-
86
- return locale;
87
- };
88
-
89
- const dalleConfig = (s: GlobalStore) => s.settings.tool?.dalle || {};
90
- const isDalleAutoGenerating = (s: GlobalStore) => s.settings.tool?.dalle?.autoGenerate;
91
-
92
- export const settingsSelectors = {
93
- currentLanguage,
94
- currentSettings,
95
- currentTTS,
96
- dalleConfig,
97
- defaultAgent,
98
- defaultAgentConfig,
99
- defaultAgentMeta,
100
- exportSettings,
101
- isDalleAutoGenerating,
102
- modelList: modelListSelectors,
103
- openAIAPI: openAIAPIKeySelectors,
104
- openAIProxyUrl: openAIProxyUrlSelectors,
105
- };
@@ -1,68 +0,0 @@
1
- import type { NeutralColors, PrimaryColors } from '@lobehub/ui';
2
- import type { ThemeMode } from 'antd-style';
3
-
4
- import { LocaleMode } from '@/types/locale';
5
- import type { LobeAgentSession } from '@/types/session';
6
-
7
- export interface GlobalBaseSettings {
8
- avatar: string;
9
- fontSize: number;
10
- language: LocaleMode;
11
- neutralColor?: NeutralColors;
12
- password: string;
13
- primaryColor?: PrimaryColors;
14
- themeMode: ThemeMode;
15
- }
16
-
17
- export type GlobalDefaultAgent = Pick<LobeAgentSession, 'config' | 'meta'>;
18
-
19
- export type CustomModels = { displayName: string; name: string }[];
20
-
21
- export interface OpenAIConfig {
22
- OPENAI_API_KEY: string;
23
- azureApiVersion?: string;
24
- /**
25
- * custom mode name for fine-tuning or openai like model
26
- */
27
- customModelName?: string;
28
- endpoint?: string;
29
- models?: string[];
30
- useAzure?: boolean;
31
- }
32
-
33
- export interface GlobalLLMConfig {
34
- openAI: OpenAIConfig;
35
- }
36
-
37
- export type STTServer = 'openai' | 'browser';
38
- export interface GlobalTTSConfig {
39
- openAI: {
40
- sttModel: 'whisper-1';
41
- ttsModel: 'tts-1' | 'tts-1-hd';
42
- };
43
- sttAutoStop: boolean;
44
- sttServer: STTServer;
45
- }
46
-
47
- export type LLMBrand = keyof GlobalLLMConfig;
48
-
49
- export interface GlobalTool {
50
- dalle: {
51
- autoGenerate: boolean;
52
- };
53
- }
54
- /**
55
- * 配置设置
56
- */
57
- export interface GlobalSettings extends GlobalBaseSettings {
58
- defaultAgent: GlobalDefaultAgent;
59
- languageModel: GlobalLLMConfig;
60
- tool: GlobalTool;
61
- tts: GlobalTTSConfig;
62
- }
63
-
64
- export type ConfigKeys = keyof GlobalSettings;
65
-
66
- export interface GlobalServerConfig {
67
- customModelName?: string;
68
- }