@lobehub/chat 0.72.4 → 0.73.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/CHANGELOG.md +25 -0
- package/next.config.mjs +6 -2
- package/package.json +5 -10
- package/src/config/server.ts +2 -0
- package/src/const/fetch.ts +15 -2
- package/src/const/llm.ts +17 -0
- package/src/const/settings.ts +16 -8
- package/src/locales/default/setting.ts +59 -13
- package/src/pages/api/{openai.ts → createChatCompletion.ts} +7 -36
- package/src/pages/api/openai/chat.api.ts +43 -0
- package/src/pages/api/openai/createAzureOpenai.ts +26 -0
- package/src/pages/api/openai/createOpenai.ts +17 -0
- package/src/pages/api/openai/models.api.ts +17 -0
- package/src/pages/settings/features/Settings/Common.tsx +2 -26
- package/src/pages/settings/features/Settings/LLM/Checker.tsx +85 -0
- package/src/pages/settings/features/Settings/LLM/ModelList.tsx +51 -0
- package/src/pages/settings/features/Settings/LLM/getModelList.ts +9 -0
- package/src/pages/settings/features/Settings/LLM/index.tsx +152 -0
- package/src/pages/settings/features/Settings/index.tsx +13 -12
- package/src/services/_header.ts +33 -0
- package/src/services/_url.ts +17 -0
- package/src/services/chatModel.ts +4 -10
- package/src/services/modelList.ts +15 -0
- package/src/services/plugin.ts +1 -1
- package/src/services/pluginMarket.ts +1 -1
- package/src/store/global/hooks/index.ts +1 -0
- package/src/store/global/hooks/useEffectAfterHydrated.ts +22 -0
- package/src/store/global/initialState.ts +4 -0
- package/src/store/global/selectors.test.ts +264 -0
- package/src/store/global/selectors.ts +4 -5
- package/src/store/global/slices/settings.ts +16 -4
- package/src/store/global/store.ts +34 -5
- package/src/types/settings.ts +45 -8
- package/src/utils/fetch.ts +4 -12
- package/src/pages/api/openai.api.ts +0 -24
- package/src/services/url.ts +0 -8
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { ThemeMode } from 'antd-style';
|
|
2
|
+
import isEqual from 'fast-deep-equal';
|
|
2
3
|
import { produce } from 'immer';
|
|
3
|
-
import {
|
|
4
|
+
import { DeepPartial } from 'utility-types';
|
|
4
5
|
import type { StateCreator } from 'zustand/vanilla';
|
|
5
6
|
|
|
6
7
|
import { DEFAULT_AGENT, DEFAULT_SETTINGS } from '@/const/settings';
|
|
8
|
+
import { SettingsTabs } from '@/store/global/initialState';
|
|
7
9
|
import type { GlobalSettings } from '@/types/settings';
|
|
10
|
+
import { merge } from '@/utils/merge';
|
|
8
11
|
import { setNamespace } from '@/utils/storeDebug';
|
|
9
12
|
|
|
10
13
|
import type { GlobalStore } from '../store';
|
|
@@ -24,7 +27,8 @@ export interface SettingsAction {
|
|
|
24
27
|
* 设置部分配置设置
|
|
25
28
|
* @param settings - 部分配置设置
|
|
26
29
|
*/
|
|
27
|
-
setSettings: (settings:
|
|
30
|
+
setSettings: (settings: DeepPartial<GlobalSettings>) => void;
|
|
31
|
+
switchSettingTabs: (tab: SettingsTabs) => void;
|
|
28
32
|
/**
|
|
29
33
|
* 设置主题模式
|
|
30
34
|
* @param themeMode - 主题模式
|
|
@@ -58,11 +62,19 @@ export const createSettingsSlice: StateCreator<
|
|
|
58
62
|
resetSettings: () => {
|
|
59
63
|
set({ settings: DEFAULT_SETTINGS }, false, t('resetSettings'));
|
|
60
64
|
},
|
|
61
|
-
|
|
62
65
|
setSettings: (settings) => {
|
|
63
66
|
const oldSetting = get().settings;
|
|
64
|
-
|
|
67
|
+
const nextSettings = merge(oldSetting, settings);
|
|
68
|
+
|
|
69
|
+
if (isEqual(oldSetting, nextSettings)) return;
|
|
70
|
+
|
|
71
|
+
set({ settings: merge(oldSetting, settings) }, false, t('setSettings', settings));
|
|
65
72
|
},
|
|
73
|
+
|
|
74
|
+
switchSettingTabs: (tab) => {
|
|
75
|
+
set({ settingsTab: tab });
|
|
76
|
+
},
|
|
77
|
+
|
|
66
78
|
switchThemeMode: (themeMode) => {
|
|
67
79
|
get().setSettings({ themeMode });
|
|
68
80
|
},
|
|
@@ -4,9 +4,10 @@ import { shallow } from 'zustand/shallow';
|
|
|
4
4
|
import { createWithEqualityFn } from 'zustand/traditional';
|
|
5
5
|
import { StateCreator } from 'zustand/vanilla';
|
|
6
6
|
|
|
7
|
-
import { DEFAULT_AGENT } from '@/const/settings';
|
|
7
|
+
import { DEFAULT_AGENT, DEFAULT_LLM_CONFIG } from '@/const/settings';
|
|
8
8
|
import { isDev } from '@/utils/env';
|
|
9
9
|
|
|
10
|
+
import { createHyperStorage } from '../middleware/createHyperStorage';
|
|
10
11
|
import { type GlobalState, initialState } from './initialState';
|
|
11
12
|
import { type AgentAction, createAgentSlice } from './slices/agent';
|
|
12
13
|
import { type CommonAction, createCommonSlice } from './slices/common';
|
|
@@ -29,6 +30,7 @@ type GlobalPersist = Pick<GlobalStore, 'preference' | 'settings'>;
|
|
|
29
30
|
const persistOptions: PersistOptions<GlobalStore, GlobalPersist> = {
|
|
30
31
|
merge: (persistedState, currentState) => {
|
|
31
32
|
const state = persistedState as GlobalPersist;
|
|
33
|
+
|
|
32
34
|
return {
|
|
33
35
|
...currentState,
|
|
34
36
|
...state,
|
|
@@ -36,15 +38,42 @@ const persistOptions: PersistOptions<GlobalStore, GlobalPersist> = {
|
|
|
36
38
|
if (!draft.defaultAgent) {
|
|
37
39
|
draft.defaultAgent = DEFAULT_AGENT;
|
|
38
40
|
}
|
|
41
|
+
delete draft.enableMaxTokens;
|
|
42
|
+
delete draft.enableHistoryCount;
|
|
43
|
+
delete draft.historyCount;
|
|
44
|
+
delete draft.enableCompressThreshold;
|
|
45
|
+
delete draft.compressThreshold;
|
|
46
|
+
|
|
47
|
+
// migration to new data model
|
|
48
|
+
if (!draft.languageModel) {
|
|
49
|
+
draft.languageModel = {
|
|
50
|
+
openAI: {
|
|
51
|
+
...DEFAULT_LLM_CONFIG.openAI,
|
|
52
|
+
OPENAI_API_KEY: draft.OPENAI_API_KEY || DEFAULT_LLM_CONFIG.openAI.OPENAI_API_KEY,
|
|
53
|
+
endpoint: draft.endpoint || DEFAULT_LLM_CONFIG.openAI.OPENAI_API_KEY,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
delete draft.OPENAI_API_KEY;
|
|
58
|
+
delete draft.endpoint;
|
|
59
|
+
}
|
|
39
60
|
}),
|
|
40
61
|
};
|
|
41
62
|
},
|
|
42
63
|
name: 'LOBE_SETTINGS',
|
|
43
|
-
|
|
44
|
-
preference: s.preference,
|
|
45
|
-
settings: s.settings,
|
|
46
|
-
}),
|
|
64
|
+
|
|
47
65
|
skipHydration: true,
|
|
66
|
+
|
|
67
|
+
storage: createHyperStorage({
|
|
68
|
+
localStorage: {
|
|
69
|
+
dbName: 'LobeHub',
|
|
70
|
+
selectors: ['preference', 'settings'],
|
|
71
|
+
},
|
|
72
|
+
url: {
|
|
73
|
+
mode: 'hash',
|
|
74
|
+
selectors: [{ settingsTab: 'tab' }],
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
48
77
|
};
|
|
49
78
|
|
|
50
79
|
// =============== 实装 useStore ============ //
|
package/src/types/settings.ts
CHANGED
|
@@ -5,15 +5,37 @@ import type { Locales } from '@/locales/resources';
|
|
|
5
5
|
import type { LobeAgentSession } from '@/types/session';
|
|
6
6
|
|
|
7
7
|
export interface GlobalBaseSettings {
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated
|
|
10
|
+
*/
|
|
11
|
+
OPENAI_API_KEY?: string;
|
|
12
|
+
|
|
9
13
|
avatar: string;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated
|
|
16
|
+
*/
|
|
17
|
+
compressThreshold?: number;
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated
|
|
20
|
+
*/
|
|
21
|
+
enableCompressThreshold?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* @deprecated
|
|
24
|
+
*/
|
|
25
|
+
enableHistoryCount?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated
|
|
28
|
+
*/
|
|
29
|
+
enableMaxTokens?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* @deprecated
|
|
32
|
+
*/
|
|
33
|
+
endpoint?: string;
|
|
15
34
|
fontSize: number;
|
|
16
|
-
|
|
35
|
+
/**
|
|
36
|
+
* @deprecated
|
|
37
|
+
*/
|
|
38
|
+
historyCount?: number;
|
|
17
39
|
language: Locales;
|
|
18
40
|
neutralColor: NeutralColors | '';
|
|
19
41
|
password: string;
|
|
@@ -21,13 +43,28 @@ export interface GlobalBaseSettings {
|
|
|
21
43
|
themeMode: ThemeMode;
|
|
22
44
|
}
|
|
23
45
|
|
|
24
|
-
export type GlobalDefaultAgent =
|
|
46
|
+
export type GlobalDefaultAgent = Pick<LobeAgentSession, 'config' | 'meta'>;
|
|
47
|
+
|
|
48
|
+
interface OpenAIConfig {
|
|
49
|
+
OPENAI_API_KEY: string;
|
|
50
|
+
azureApiVersion?: string;
|
|
51
|
+
endpoint?: string;
|
|
52
|
+
models?: string[];
|
|
53
|
+
useAzure?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type GlobalLLMConfig = {
|
|
57
|
+
openAI: OpenAIConfig;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export type LLMBrand = keyof GlobalLLMConfig;
|
|
25
61
|
|
|
26
62
|
/**
|
|
27
63
|
* 配置设置
|
|
28
64
|
*/
|
|
29
65
|
export interface GlobalSettings extends GlobalBaseSettings {
|
|
30
66
|
defaultAgent: GlobalDefaultAgent;
|
|
67
|
+
languageModel: GlobalLLMConfig;
|
|
31
68
|
}
|
|
32
69
|
|
|
33
70
|
export type ConfigKeys = keyof GlobalSettings;
|
package/src/utils/fetch.ts
CHANGED
|
@@ -74,7 +74,7 @@ interface FetchAITaskResultParams<T> {
|
|
|
74
74
|
/**
|
|
75
75
|
* 错误处理函数
|
|
76
76
|
*/
|
|
77
|
-
onError?: (e: Error) => void;
|
|
77
|
+
onError?: (e: Error, rawError?: any) => void;
|
|
78
78
|
/**
|
|
79
79
|
* 加载状态变化处理函数
|
|
80
80
|
* @param loading - 是否处于加载状态
|
|
@@ -101,27 +101,19 @@ export const fetchAIFactory =
|
|
|
101
101
|
onLoadingChange,
|
|
102
102
|
abortController,
|
|
103
103
|
}: FetchAITaskResultParams<T>) => {
|
|
104
|
-
const errorHandle = (error: Error) => {
|
|
104
|
+
const errorHandle = (error: Error, errorContent?: any) => {
|
|
105
105
|
onLoadingChange?.(false);
|
|
106
106
|
if (abortController?.signal.aborted) {
|
|
107
|
-
// notification.primaryInfo({
|
|
108
|
-
// message: '已中断当前节点的执行任务',
|
|
109
|
-
// });
|
|
110
107
|
return;
|
|
111
108
|
}
|
|
112
|
-
|
|
113
|
-
// notification?.error({
|
|
114
|
-
// message: `请求失败(${error.message})`,
|
|
115
|
-
// placement: 'bottomRight',
|
|
116
|
-
// });
|
|
117
|
-
onError?.(error);
|
|
109
|
+
onError?.(error, errorContent);
|
|
118
110
|
};
|
|
119
111
|
|
|
120
112
|
onLoadingChange?.(true);
|
|
121
113
|
|
|
122
114
|
const data = await fetchSSE(() => fetcher(params, { signal: abortController?.signal }), {
|
|
123
115
|
onErrorHandle: (error) => {
|
|
124
|
-
errorHandle(new Error(error.message));
|
|
116
|
+
errorHandle(new Error(error.message), error);
|
|
125
117
|
},
|
|
126
118
|
onMessageHandle,
|
|
127
119
|
}).catch(errorHandle);
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { LOBE_CHAT_ACCESS_CODE, OPENAI_API_KEY_HEADER_KEY, OPENAI_END_POINT } from '@/const/fetch';
|
|
2
|
-
import { ErrorType } from '@/types/fetch';
|
|
3
|
-
import { OpenAIStreamPayload } from '@/types/openai';
|
|
4
|
-
|
|
5
|
-
import { checkAuth } from './auth';
|
|
6
|
-
import { createErrorResponse } from './error';
|
|
7
|
-
import { createChatCompletion } from './openai';
|
|
8
|
-
|
|
9
|
-
export const runtime = 'edge';
|
|
10
|
-
|
|
11
|
-
export default async function handler(req: Request) {
|
|
12
|
-
const payload = (await req.json()) as OpenAIStreamPayload;
|
|
13
|
-
const apiKey = req.headers.get(OPENAI_API_KEY_HEADER_KEY);
|
|
14
|
-
const accessCode = req.headers.get(LOBE_CHAT_ACCESS_CODE);
|
|
15
|
-
const endpoint = req.headers.get(OPENAI_END_POINT);
|
|
16
|
-
|
|
17
|
-
const result = checkAuth({ accessCode, apiKey });
|
|
18
|
-
|
|
19
|
-
if (!result.auth) {
|
|
20
|
-
return createErrorResponse(result.error as ErrorType);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return createChatCompletion({ OPENAI_API_KEY: apiKey, endpoint, payload });
|
|
24
|
-
}
|