@lobehub/chat 0.72.3 → 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 +50 -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/const/url.ts +2 -1
- package/src/locales/default/setting.ts +59 -13
- package/src/locales/options.ts +1 -1
- 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 +4 -28
- 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
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 0.73.0](https://github.com/lobehub/lobe-chat/compare/v0.72.4...v0.73.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-09-10**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Support Azure OpenAI.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: Support Azure OpenAI, closes [#177](https://github.com/lobehub/lobe-chat/issues/177) ([f0c9532](https://github.com/lobehub/lobe-chat/commit/f0c9532))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
### [Version 0.72.4](https://github.com/lobehub/lobe-chat/compare/v0.72.3...v0.72.4)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2023-09-10**</sup>
|
|
33
|
+
|
|
34
|
+
#### 🐛 Bug Fixes
|
|
35
|
+
|
|
36
|
+
- **misc**: Use en-US when no suit lang with plugin index.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### What's fixed
|
|
44
|
+
|
|
45
|
+
- **misc**: Use en-US when no suit lang with plugin index ([4e9668d](https://github.com/lobehub/lobe-chat/commit/4e9668d))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
### [Version 0.72.3](https://github.com/lobehub/lobe-chat/compare/v0.72.2...v0.72.3)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2023-09-09**</sup>
|
package/next.config.mjs
CHANGED
|
@@ -27,8 +27,12 @@ const nextConfig = {
|
|
|
27
27
|
async rewrites() {
|
|
28
28
|
return [
|
|
29
29
|
{
|
|
30
|
-
source: '/api/openai-dev',
|
|
31
|
-
destination: `${API_END_PORT_URL}/api/openai`,
|
|
30
|
+
source: '/api/openai/chat-dev',
|
|
31
|
+
destination: `${API_END_PORT_URL}/api/openai/chat`,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
source: '/api/openai/models-dev',
|
|
35
|
+
destination: `${API_END_PORT_URL}/api/openai/models`,
|
|
32
36
|
},
|
|
33
37
|
{
|
|
34
38
|
source: '/api/plugins-dev',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.73.0",
|
|
4
4
|
"description": "Lobe Chat is an open-source chatbot client using Vercel AI SDK, Typescript and Next.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chatbot",
|
|
@@ -65,11 +65,12 @@
|
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@ant-design/colors": "^7",
|
|
67
67
|
"@ant-design/icons": "^5",
|
|
68
|
+
"@azure/openai": "latest",
|
|
68
69
|
"@emoji-mart/data": "^1",
|
|
69
70
|
"@emoji-mart/react": "^1",
|
|
70
71
|
"@icons-pack/react-simple-icons": "^9",
|
|
71
|
-
"@lobehub/chat-plugin-sdk": "^1.17.
|
|
72
|
-
"@lobehub/chat-plugins-gateway": "^1.5.
|
|
72
|
+
"@lobehub/chat-plugin-sdk": "^1.17.7",
|
|
73
|
+
"@lobehub/chat-plugins-gateway": "^1.5.1",
|
|
73
74
|
"@lobehub/ui": "latest",
|
|
74
75
|
"@vercel/analytics": "^1",
|
|
75
76
|
"ahooks": "^3",
|
|
@@ -96,11 +97,11 @@
|
|
|
96
97
|
"react-i18next": "^13",
|
|
97
98
|
"react-intersection-observer": "^9",
|
|
98
99
|
"react-layout-kit": "^1.7.1",
|
|
99
|
-
"serpapi": "^2",
|
|
100
100
|
"swr": "^2",
|
|
101
101
|
"systemjs": "^6.14.2",
|
|
102
102
|
"ts-md5": "^1",
|
|
103
103
|
"use-merge-value": "^1",
|
|
104
|
+
"utility-types": "^3",
|
|
104
105
|
"uuid": "^9",
|
|
105
106
|
"zustand": "^4.4",
|
|
106
107
|
"zustand-utils": "^1"
|
|
@@ -142,12 +143,6 @@
|
|
|
142
143
|
"typescript": "^5",
|
|
143
144
|
"vitest": "latest"
|
|
144
145
|
},
|
|
145
|
-
"peerDependencies": {
|
|
146
|
-
"antd": ">=5",
|
|
147
|
-
"antd-style": ">=3",
|
|
148
|
-
"react": ">=18",
|
|
149
|
-
"react-dom": ">=18"
|
|
150
|
-
},
|
|
151
146
|
"publishConfig": {
|
|
152
147
|
"access": "public",
|
|
153
148
|
"registry": "https://registry.npmjs.org"
|
package/src/config/server.ts
CHANGED
|
@@ -3,6 +3,7 @@ declare global {
|
|
|
3
3
|
namespace NodeJS {
|
|
4
4
|
interface ProcessEnv {
|
|
5
5
|
ACCESS_CODE?: string;
|
|
6
|
+
AZURE_API_KEY?: string;
|
|
6
7
|
OPENAI_API_KEY?: string;
|
|
7
8
|
OPENAI_PROXY_URL?: string;
|
|
8
9
|
}
|
|
@@ -16,6 +17,7 @@ export const getServerConfig = () => {
|
|
|
16
17
|
|
|
17
18
|
return {
|
|
18
19
|
ACCESS_CODE: process.env.ACCESS_CODE,
|
|
20
|
+
AZURE_API_KEY: process.env.AZURE_API_KEY,
|
|
19
21
|
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
|
20
22
|
OPENAI_PROXY_URL: process.env.OPENAI_PROXY_URL,
|
|
21
23
|
};
|
package/src/const/fetch.ts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
export const OPENAI_END_POINT = 'X-OPENAI-END_POINT';
|
|
2
|
-
|
|
3
2
|
export const OPENAI_API_KEY_HEADER_KEY = 'X-OPENAI-API-KEY';
|
|
4
3
|
|
|
4
|
+
export const USE_AZURE_OPENAI = 'X-USE_AZURE_OPENAI';
|
|
5
|
+
|
|
6
|
+
export const AZURE_OPENAI_API_VERSION = 'X-AZURE_OPENAI_API_VERSION';
|
|
7
|
+
|
|
5
8
|
export const LOBE_CHAT_ACCESS_CODE = 'X-LOBE_CHAT_ACCESS_CODE';
|
|
6
9
|
|
|
7
|
-
export const
|
|
10
|
+
export const getOpenAIAuthFromRequest = (req: Request) => {
|
|
11
|
+
const apiKey = req.headers.get(OPENAI_API_KEY_HEADER_KEY);
|
|
12
|
+
const endpoint = req.headers.get(OPENAI_END_POINT);
|
|
13
|
+
const accessCode = req.headers.get(LOBE_CHAT_ACCESS_CODE);
|
|
14
|
+
const useAzureStr = req.headers.get(USE_AZURE_OPENAI);
|
|
15
|
+
const apiVersion = req.headers.get(AZURE_OPENAI_API_VERSION);
|
|
16
|
+
|
|
17
|
+
const useAzure = !!useAzureStr;
|
|
18
|
+
|
|
19
|
+
return { accessCode, apiKey, apiVersion, endpoint, useAzure };
|
|
20
|
+
};
|
package/src/const/llm.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A white list of language models that are allowed to display and be used in the app.
|
|
3
|
+
*/
|
|
4
|
+
export const LanguageModelWhiteList = [
|
|
5
|
+
// OpenAI
|
|
6
|
+
'gpt-3.5-turbo',
|
|
7
|
+
'gpt-3.5-turbo-16k',
|
|
8
|
+
'gpt-4',
|
|
9
|
+
'gpt-4-32k',
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export const DEFAULT_OPENAI_MODEL_LIST = [
|
|
13
|
+
'gpt-3.5-turbo',
|
|
14
|
+
'gpt-3.5-turbo-16k',
|
|
15
|
+
'gpt-4',
|
|
16
|
+
'gpt-4-32k',
|
|
17
|
+
];
|
package/src/const/settings.ts
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
|
+
import { DEFAULT_OPENAI_MODEL_LIST } from '@/const/llm';
|
|
1
2
|
import { DEFAULT_AGENT_META } from '@/const/meta';
|
|
2
3
|
import { LanguageModel } from '@/types/llm';
|
|
3
4
|
import { LobeAgentConfig } from '@/types/session';
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
GlobalBaseSettings,
|
|
7
|
+
GlobalDefaultAgent,
|
|
8
|
+
GlobalLLMConfig,
|
|
9
|
+
GlobalSettings,
|
|
10
|
+
} from '@/types/settings';
|
|
5
11
|
|
|
6
12
|
export const DEFAULT_BASE_SETTINGS: GlobalBaseSettings = {
|
|
7
|
-
OPENAI_API_KEY: '',
|
|
8
13
|
avatar: '',
|
|
9
|
-
compressThreshold: 24,
|
|
10
|
-
enableCompressThreshold: false,
|
|
11
|
-
enableHistoryCount: false,
|
|
12
|
-
enableMaxTokens: true,
|
|
13
|
-
endpoint: '',
|
|
14
14
|
fontSize: 14,
|
|
15
|
-
historyCount: 24,
|
|
16
15
|
language: 'zh-CN',
|
|
17
16
|
neutralColor: '',
|
|
18
17
|
password: '',
|
|
@@ -34,6 +33,14 @@ export const DEFAULT_AGENT_CONFIG: LobeAgentConfig = {
|
|
|
34
33
|
systemRole: '',
|
|
35
34
|
};
|
|
36
35
|
|
|
36
|
+
export const DEFAULT_LLM_CONFIG: GlobalLLMConfig = {
|
|
37
|
+
openAI: {
|
|
38
|
+
OPENAI_API_KEY: '',
|
|
39
|
+
azureApiVersion: '2023-08-01-preview',
|
|
40
|
+
models: DEFAULT_OPENAI_MODEL_LIST,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
37
44
|
export const DEFAULT_AGENT: GlobalDefaultAgent = {
|
|
38
45
|
config: DEFAULT_AGENT_CONFIG,
|
|
39
46
|
meta: DEFAULT_AGENT_META,
|
|
@@ -41,5 +48,6 @@ export const DEFAULT_AGENT: GlobalDefaultAgent = {
|
|
|
41
48
|
|
|
42
49
|
export const DEFAULT_SETTINGS: GlobalSettings = {
|
|
43
50
|
defaultAgent: DEFAULT_AGENT,
|
|
51
|
+
languageModel: DEFAULT_LLM_CONFIG,
|
|
44
52
|
...DEFAULT_BASE_SETTINGS,
|
|
45
53
|
};
|
package/src/const/url.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { localeOptions } from '@/locales/options';
|
|
1
2
|
import { Locales } from '@/locales/resources';
|
|
2
3
|
|
|
3
4
|
import pkg from '../../package.json';
|
|
@@ -13,7 +14,7 @@ export const PLUGINS_INDEX_URL =
|
|
|
13
14
|
process.env.PLUGINS_INDEX_URL ?? 'https://chat-plugins.lobehub.com/index';
|
|
14
15
|
|
|
15
16
|
export const getPluginIndexJSON = (lang: Locales = 'en-US', baseUrl = PLUGINS_INDEX_URL) => {
|
|
16
|
-
if (lang === 'en-US') return baseUrl;
|
|
17
|
+
if (lang === 'en-US' || !localeOptions.map((o) => o.value).includes(lang)) return baseUrl;
|
|
17
18
|
|
|
18
19
|
return `${baseUrl}.${lang}.json`;
|
|
19
20
|
};
|
|
@@ -20,6 +20,64 @@ export default {
|
|
|
20
20
|
session: '会话设置',
|
|
21
21
|
sessionWithName: '会话设置 · {{name}}',
|
|
22
22
|
},
|
|
23
|
+
llm: {
|
|
24
|
+
AzureOpenAI: {
|
|
25
|
+
endpoint: {
|
|
26
|
+
desc: '从 Azure 门户检查资源时,可在“密钥和终结点”部分中找到此值',
|
|
27
|
+
placeholder: 'https://docs-test-001.openai.azure.com',
|
|
28
|
+
title: 'Azure API 地址',
|
|
29
|
+
},
|
|
30
|
+
models: {
|
|
31
|
+
desc: '支持的模型',
|
|
32
|
+
title: '模型列表',
|
|
33
|
+
},
|
|
34
|
+
title: 'Azure OpenAI 设置',
|
|
35
|
+
token: {
|
|
36
|
+
desc: '从 Azure 门户检查资源时,可在“密钥和终结点”部分中找到此值。 可以使用 KEY1 或 KEY2',
|
|
37
|
+
placeholder: 'Azure API Key',
|
|
38
|
+
title: 'API Key',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
OpenAI: {
|
|
42
|
+
azureApiVersion: {
|
|
43
|
+
desc: 'Azure 的 API 版本,遵循 YYYY-MM-DD 格式,查阅[最新版本](https://learn.microsoft.com/zh-cn/azure/ai-services/openai/reference#chat-completions)',
|
|
44
|
+
fetch: '获取列表',
|
|
45
|
+
title: 'Azure Api Version',
|
|
46
|
+
},
|
|
47
|
+
check: {
|
|
48
|
+
button: '检查',
|
|
49
|
+
desc: '测试 Api Key 与代理地址是否正确填写',
|
|
50
|
+
pass: '检查通过',
|
|
51
|
+
title: '连通性检查',
|
|
52
|
+
},
|
|
53
|
+
endpoint: {
|
|
54
|
+
desc: '除默认地址外,必须包含 http(s)://',
|
|
55
|
+
placeholder: 'https://api.openai.com/v1',
|
|
56
|
+
title: '接口代理地址',
|
|
57
|
+
},
|
|
58
|
+
models: {
|
|
59
|
+
count: '共支持 {{count}} 个模型',
|
|
60
|
+
desc: '支持的模型',
|
|
61
|
+
fetch: '获取模型列表',
|
|
62
|
+
notSupport: 'Azure OpenAI 暂不支持查看模型列表',
|
|
63
|
+
notSupportTip: '你需要自行确保部署名称与模型名称一致',
|
|
64
|
+
refetch: '重新获取模型列表',
|
|
65
|
+
title: '模型列表',
|
|
66
|
+
},
|
|
67
|
+
title: 'OpenAI 设置',
|
|
68
|
+
token: {
|
|
69
|
+
desc: '使用自己的 OpenAI Key',
|
|
70
|
+
placeholder: 'OpenAI API Key',
|
|
71
|
+
title: 'API Key',
|
|
72
|
+
},
|
|
73
|
+
useAzure: {
|
|
74
|
+
desc: '使用 Azure 提供的 OpenAI 服务',
|
|
75
|
+
fetch: '获取列表',
|
|
76
|
+
title: 'Azure OpenAI',
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
waitingForMore: '更多模型正在 <1>计划接入</1> 中,敬请期待 ✨',
|
|
80
|
+
},
|
|
23
81
|
settingAgent: {
|
|
24
82
|
avatar: {
|
|
25
83
|
title: '头像',
|
|
@@ -114,19 +172,6 @@ export default {
|
|
|
114
172
|
title: '核采样',
|
|
115
173
|
},
|
|
116
174
|
},
|
|
117
|
-
settingOpenAI: {
|
|
118
|
-
endpoint: {
|
|
119
|
-
desc: '除默认地址外,必须包含 http(s)://',
|
|
120
|
-
placeholder: 'https://api.openai.com/v1',
|
|
121
|
-
title: '接口代理地址',
|
|
122
|
-
},
|
|
123
|
-
title: 'OpenAI 设置',
|
|
124
|
-
token: {
|
|
125
|
-
desc: '使用自己的 OpenAI Key',
|
|
126
|
-
placeholder: 'OpenAI API Key',
|
|
127
|
-
title: 'API Key',
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
175
|
settingPlugin: {
|
|
131
176
|
add: '添加',
|
|
132
177
|
addTooltip: '添加自定义插件',
|
|
@@ -173,5 +218,6 @@ export default {
|
|
|
173
218
|
tab: {
|
|
174
219
|
agent: '默认助手',
|
|
175
220
|
common: '通用设置',
|
|
221
|
+
llm: '语言模型',
|
|
176
222
|
},
|
|
177
223
|
};
|
package/src/locales/options.ts
CHANGED
|
@@ -1,46 +1,17 @@
|
|
|
1
1
|
import { OpenAIStream, StreamingTextResponse } from 'ai';
|
|
2
|
-
import OpenAI
|
|
2
|
+
import OpenAI from 'openai';
|
|
3
3
|
|
|
4
|
-
import { getServerConfig } from '@/config/server';
|
|
5
4
|
import { createErrorResponse } from '@/pages/api/error';
|
|
6
5
|
import { ChatErrorType } from '@/types/fetch';
|
|
7
6
|
import { OpenAIStreamPayload } from '@/types/openai';
|
|
8
7
|
|
|
9
|
-
// 创建 OpenAI 实例
|
|
10
|
-
export const createOpenAI = (userApiKey: string | null, endpoint?: string | null) => {
|
|
11
|
-
const { OPENAI_API_KEY, OPENAI_PROXY_URL } = getServerConfig();
|
|
12
|
-
|
|
13
|
-
const baseURL = endpoint ? endpoint : OPENAI_PROXY_URL ? OPENAI_PROXY_URL : undefined;
|
|
14
|
-
|
|
15
|
-
const config: ClientOptions = {
|
|
16
|
-
apiKey: !userApiKey ? OPENAI_API_KEY : userApiKey,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// a bug with openai: https://github.com/openai/openai-node/issues/283
|
|
20
|
-
// TODO: should refactor when openai fix the bug
|
|
21
|
-
if (baseURL) {
|
|
22
|
-
config.baseURL = baseURL;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return new OpenAI(config);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
8
|
interface CreateChatCompletionOptions {
|
|
29
|
-
|
|
30
|
-
endpoint?: string | null;
|
|
9
|
+
openai: OpenAI;
|
|
31
10
|
payload: OpenAIStreamPayload;
|
|
32
11
|
}
|
|
33
12
|
|
|
34
|
-
export const createChatCompletion = async ({
|
|
35
|
-
|
|
36
|
-
OPENAI_API_KEY,
|
|
37
|
-
endpoint,
|
|
38
|
-
}: CreateChatCompletionOptions) => {
|
|
39
|
-
// ============ 0.创建 OpenAI 实例 ============ //
|
|
40
|
-
|
|
41
|
-
const openai = createOpenAI(OPENAI_API_KEY, endpoint);
|
|
42
|
-
|
|
43
|
-
// ============ 1. 前置处理 messages ============ //
|
|
13
|
+
export const createChatCompletion = async ({ payload, openai }: CreateChatCompletionOptions) => {
|
|
14
|
+
// ============ 1. preprocess messages ============ //
|
|
44
15
|
const { messages, ...params } = payload;
|
|
45
16
|
|
|
46
17
|
const formatMessages = messages.map((m) => ({
|
|
@@ -49,7 +20,7 @@ export const createChatCompletion = async ({
|
|
|
49
20
|
role: m.role,
|
|
50
21
|
}));
|
|
51
22
|
|
|
52
|
-
// ============ 2.
|
|
23
|
+
// ============ 2. send api ============ //
|
|
53
24
|
|
|
54
25
|
try {
|
|
55
26
|
const response = await openai.chat.completions.create({
|
|
@@ -63,7 +34,7 @@ export const createChatCompletion = async ({
|
|
|
63
34
|
// Check if the error is an OpenAI APIError
|
|
64
35
|
if (error instanceof OpenAI.APIError) {
|
|
65
36
|
return createErrorResponse(ChatErrorType.OpenAIBizError, {
|
|
66
|
-
endpoint:
|
|
37
|
+
endpoint: openai.baseURL,
|
|
67
38
|
error: error.error ?? error.cause,
|
|
68
39
|
});
|
|
69
40
|
}
|
|
@@ -73,7 +44,7 @@ export const createChatCompletion = async ({
|
|
|
73
44
|
|
|
74
45
|
// return as a GatewayTimeout error
|
|
75
46
|
return createErrorResponse(ChatErrorType.InternalServerError, {
|
|
76
|
-
endpoint,
|
|
47
|
+
endpoint: openai.baseURL,
|
|
77
48
|
error: JSON.stringify(error),
|
|
78
49
|
});
|
|
79
50
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
|
|
3
|
+
import { getOpenAIAuthFromRequest } from '@/const/fetch';
|
|
4
|
+
import { ChatErrorType, ErrorType } from '@/types/fetch';
|
|
5
|
+
import { OpenAIStreamPayload } from '@/types/openai';
|
|
6
|
+
|
|
7
|
+
import { checkAuth } from '../auth';
|
|
8
|
+
import { createChatCompletion } from '../createChatCompletion';
|
|
9
|
+
import { createErrorResponse } from '../error';
|
|
10
|
+
import { createAzureOpenai } from './createAzureOpenai';
|
|
11
|
+
import { createOpenai } from './createOpenai';
|
|
12
|
+
|
|
13
|
+
export const runtime = 'edge';
|
|
14
|
+
|
|
15
|
+
export default async function handler(req: Request) {
|
|
16
|
+
const payload = (await req.json()) as OpenAIStreamPayload;
|
|
17
|
+
|
|
18
|
+
const { apiKey, accessCode, endpoint, useAzure, apiVersion } = getOpenAIAuthFromRequest(req);
|
|
19
|
+
|
|
20
|
+
const result = checkAuth({ accessCode, apiKey });
|
|
21
|
+
|
|
22
|
+
if (!result.auth) {
|
|
23
|
+
return createErrorResponse(result.error as ErrorType);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let openai: OpenAI;
|
|
27
|
+
if (useAzure) {
|
|
28
|
+
if (!apiVersion) return createErrorResponse(ChatErrorType.BadRequest);
|
|
29
|
+
|
|
30
|
+
// `https://test-001.openai.azure.com/openai/deployments/gpt-35-turbo`,
|
|
31
|
+
const url = `${endpoint}/openai/deployments/${payload.model.replace('.', '')}`;
|
|
32
|
+
|
|
33
|
+
openai = createAzureOpenai({
|
|
34
|
+
apiVersion,
|
|
35
|
+
endpoint: url,
|
|
36
|
+
userApiKey: apiKey,
|
|
37
|
+
});
|
|
38
|
+
} else {
|
|
39
|
+
openai = createOpenai(apiKey, endpoint);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return createChatCompletion({ openai, payload });
|
|
43
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import OpenAI, { ClientOptions } from 'openai';
|
|
2
|
+
|
|
3
|
+
import { getServerConfig } from '@/config/server';
|
|
4
|
+
|
|
5
|
+
// 创建 Azure OpenAI 实例
|
|
6
|
+
export const createAzureOpenai = (params: {
|
|
7
|
+
apiVersion: string;
|
|
8
|
+
endpoint: string;
|
|
9
|
+
userApiKey?: string | null;
|
|
10
|
+
}) => {
|
|
11
|
+
const { AZURE_API_KEY } = getServerConfig();
|
|
12
|
+
|
|
13
|
+
const baseURL = params.endpoint;
|
|
14
|
+
const apiKey = !params.userApiKey ? AZURE_API_KEY : params.userApiKey;
|
|
15
|
+
|
|
16
|
+
const config: ClientOptions = {
|
|
17
|
+
apiKey: apiKey,
|
|
18
|
+
baseURL,
|
|
19
|
+
defaultHeaders: { 'api-key': apiKey },
|
|
20
|
+
defaultQuery: {
|
|
21
|
+
'api-version': params.apiVersion,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return new OpenAI(config);
|
|
26
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import OpenAI, { ClientOptions } from 'openai';
|
|
2
|
+
|
|
3
|
+
import { getServerConfig } from '@/config/server';
|
|
4
|
+
|
|
5
|
+
// 创建 OpenAI 实例
|
|
6
|
+
export const createOpenai = (userApiKey: string | null, endpoint?: string | null) => {
|
|
7
|
+
const { OPENAI_API_KEY, OPENAI_PROXY_URL } = getServerConfig();
|
|
8
|
+
|
|
9
|
+
const baseURL = endpoint ? endpoint : OPENAI_PROXY_URL ? OPENAI_PROXY_URL : undefined;
|
|
10
|
+
|
|
11
|
+
const config: ClientOptions = {
|
|
12
|
+
apiKey: !userApiKey ? OPENAI_API_KEY : userApiKey,
|
|
13
|
+
baseURL,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return new OpenAI(config);
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getOpenAIAuthFromRequest } from '@/const/fetch';
|
|
2
|
+
|
|
3
|
+
import { createOpenai } from './createOpenai';
|
|
4
|
+
|
|
5
|
+
export const runtime = 'edge';
|
|
6
|
+
|
|
7
|
+
export default async function handler(req: Request) {
|
|
8
|
+
const { apiKey, endpoint } = getOpenAIAuthFromRequest(req);
|
|
9
|
+
|
|
10
|
+
const openAI = createOpenai(apiKey, endpoint);
|
|
11
|
+
|
|
12
|
+
const res = await openAI.models.list();
|
|
13
|
+
|
|
14
|
+
const modelList = res.data.map((i) => i.id);
|
|
15
|
+
|
|
16
|
+
return new Response(JSON.stringify(modelList));
|
|
17
|
+
}
|
|
@@ -3,7 +3,7 @@ import { Form as AntForm, App, Button, Input, Select } from 'antd';
|
|
|
3
3
|
import isEqual from 'fast-deep-equal';
|
|
4
4
|
import { changeLanguage } from 'i18next';
|
|
5
5
|
import { debounce } from 'lodash-es';
|
|
6
|
-
import { AppWindow, Monitor, Moon, Palette, Sun
|
|
6
|
+
import { AppWindow, Monitor, Moon, Palette, Sun } from 'lucide-react';
|
|
7
7
|
import { memo, useCallback, useMemo } from 'react';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
9
9
|
|
|
@@ -11,7 +11,7 @@ import SelectWithImg from '@/components/SelectWithImg';
|
|
|
11
11
|
import { FORM_STYLE } from '@/const/layoutTokens';
|
|
12
12
|
import { DEFAULT_SETTINGS } from '@/const/settings';
|
|
13
13
|
import AvatarWithUpload from '@/features/AvatarWithUpload';
|
|
14
|
-
import {
|
|
14
|
+
import { localeOptions } from '@/locales/options';
|
|
15
15
|
import { globalSelectors, useGlobalStore } from '@/store/global';
|
|
16
16
|
import { useSessionStore } from '@/store/session';
|
|
17
17
|
import { ConfigKeys } from '@/types/settings';
|
|
@@ -113,7 +113,7 @@ const Common = memo(() => {
|
|
|
113
113
|
onChange={(e) => {
|
|
114
114
|
changeLanguage(e);
|
|
115
115
|
}}
|
|
116
|
-
options={
|
|
116
|
+
options={localeOptions}
|
|
117
117
|
/>
|
|
118
118
|
),
|
|
119
119
|
label: t('settingTheme.lang.title'),
|
|
@@ -144,30 +144,6 @@ const Common = memo(() => {
|
|
|
144
144
|
[settings],
|
|
145
145
|
);
|
|
146
146
|
|
|
147
|
-
const openAI: SettingItemGroup = useMemo(
|
|
148
|
-
() => ({
|
|
149
|
-
children: [
|
|
150
|
-
{
|
|
151
|
-
children: <Input.Password placeholder={t('settingOpenAI.token.placeholder')} />,
|
|
152
|
-
desc: t('settingOpenAI.token.desc'),
|
|
153
|
-
label: t('settingOpenAI.token.title'),
|
|
154
|
-
name: 'OPENAI_API_KEY',
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
children: <Input placeholder={t('settingOpenAI.endpoint.placeholder')} />,
|
|
158
|
-
desc: t('settingOpenAI.endpoint.desc'),
|
|
159
|
-
|
|
160
|
-
label: t('settingOpenAI.endpoint.title'),
|
|
161
|
-
|
|
162
|
-
name: 'endpoint',
|
|
163
|
-
},
|
|
164
|
-
],
|
|
165
|
-
icon: Webhook,
|
|
166
|
-
title: t('settingOpenAI.title'),
|
|
167
|
-
}),
|
|
168
|
-
[settings],
|
|
169
|
-
);
|
|
170
|
-
|
|
171
147
|
const system: SettingItemGroup = useMemo(
|
|
172
148
|
() => ({
|
|
173
149
|
children: [
|
|
@@ -204,7 +180,7 @@ const Common = memo(() => {
|
|
|
204
180
|
[settings],
|
|
205
181
|
);
|
|
206
182
|
|
|
207
|
-
const items = useMemo(() => [theme,
|
|
183
|
+
const items = useMemo(() => [theme, system], [settings]);
|
|
208
184
|
|
|
209
185
|
return (
|
|
210
186
|
<Form
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { CheckCircleFilled } from '@ant-design/icons';
|
|
2
|
+
import { Highlighter } from '@lobehub/ui';
|
|
3
|
+
import { Alert, Button } from 'antd';
|
|
4
|
+
import { useTheme } from 'antd-style';
|
|
5
|
+
import { memo, useState } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { Flexbox } from 'react-layout-kit';
|
|
8
|
+
|
|
9
|
+
import { ChatMessageError } from '@/types/chatMessage';
|
|
10
|
+
import { fetchPresetTaskResult } from '@/utils/fetch';
|
|
11
|
+
|
|
12
|
+
import { getModelList } from './getModelList';
|
|
13
|
+
|
|
14
|
+
interface CheckerProps {
|
|
15
|
+
checkModel?: boolean;
|
|
16
|
+
}
|
|
17
|
+
const Checker = memo<CheckerProps>(({ checkModel }) => {
|
|
18
|
+
const { t } = useTranslation('setting');
|
|
19
|
+
|
|
20
|
+
const [loading, setLoading] = useState(false);
|
|
21
|
+
const [pass, setPass] = useState(false);
|
|
22
|
+
|
|
23
|
+
const theme = useTheme();
|
|
24
|
+
const [error, setError] = useState<ChatMessageError | undefined>();
|
|
25
|
+
|
|
26
|
+
const checkConnection = async () => {
|
|
27
|
+
if (checkModel) {
|
|
28
|
+
getModelList();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const data = await fetchPresetTaskResult({
|
|
32
|
+
onError: (_, rawError) => {
|
|
33
|
+
setError(rawError);
|
|
34
|
+
},
|
|
35
|
+
onLoadingChange: (loading) => {
|
|
36
|
+
setLoading(loading);
|
|
37
|
+
},
|
|
38
|
+
params: {
|
|
39
|
+
messages: [
|
|
40
|
+
{
|
|
41
|
+
content: '你好',
|
|
42
|
+
role: 'user',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
model: 'gpt-3.5-turbo',
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (data) {
|
|
50
|
+
setError(undefined);
|
|
51
|
+
setPass(true);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
return (
|
|
55
|
+
<Flexbox gap={8}>
|
|
56
|
+
<Flexbox align={'center'} gap={12} horizontal>
|
|
57
|
+
<Button loading={loading} onClick={checkConnection}>
|
|
58
|
+
{t('llm.OpenAI.check.button')}
|
|
59
|
+
</Button>
|
|
60
|
+
|
|
61
|
+
{pass && (
|
|
62
|
+
<Flexbox gap={4} horizontal>
|
|
63
|
+
<CheckCircleFilled
|
|
64
|
+
style={{
|
|
65
|
+
color: theme.colorSuccess,
|
|
66
|
+
}}
|
|
67
|
+
/>
|
|
68
|
+
{t('llm.OpenAI.check.pass')}
|
|
69
|
+
</Flexbox>
|
|
70
|
+
)}
|
|
71
|
+
</Flexbox>
|
|
72
|
+
|
|
73
|
+
{error && (
|
|
74
|
+
<Flexbox gap={8}>
|
|
75
|
+
<Alert banner message={error.message} showIcon type={'error'}></Alert>
|
|
76
|
+
<Flexbox style={{ maxWidth: 600 }}>
|
|
77
|
+
<Highlighter language={'json'}>{JSON.stringify(error.body, null, 2)}</Highlighter>
|
|
78
|
+
</Flexbox>
|
|
79
|
+
</Flexbox>
|
|
80
|
+
)}
|
|
81
|
+
</Flexbox>
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
export default Checker;
|