Skip to content

Commit bda6732

Browse files
authored
✨ feat: Support Azure OpenAI Deploy env (#183)
* ✨ feat: support azure deploy config * ♻️ refactor: refactor code * 🐛 fix: fix error * ✅ test: fix test
1 parent 03284dd commit bda6732

File tree

12 files changed

+102
-25
lines changed

12 files changed

+102
-25
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# 使用 Azure OpenAI 部署
2+
3+
LobeChat 支持使用 [Azure OpenAI][azure-openai-url] 作为 OpenAI 的模型服务商,本文将介绍如何配置 Azure OpenAI。
4+
5+
## 使用限制
6+
7+
从研发成本考虑([#178][rfc]),目前阶段的 LobeChat 并没有 100% 完全符合 Azure OpenAI 的实现模型,采用了以 `openai` 为基座,兼容 Azure OpeAI 的解决方案。因此会带来以下局限性:
8+
9+
- OpenAI 与 Azure OpenAI 只能二选一,当你开启使用 Azure OpenAI 后,将无法使用 OpenAI 作为模型服务商;
10+
- LobeChat 约定了与模型同名的部署名才能正常使用,比如 `gpt-35-turbo` 模型的部署名,必须为 `gpt-35-turbo`,否则 LobeChat 将无法正常正确匹配到相应模型
11+
![](https://github-production-user-asset-6210df.s3.amazonaws.com/28616219/267082091-d89d53d3-1c8c-40ca-ba15-0a9af2a79264.png)
12+
- 由于 Azure OpenAI 的 SDK 接入复杂度,当前无法查询配置资源的模型列表;
13+
14+
## 在界面中配置
15+
16+
点击左下角「操作」 -「设置」,切到 「语言模型」 Tab 后通过开启「Azure OpenAI」开关,即可开启使用 Azure OpenAI。
17+
18+
![](https://github-production-user-asset-6210df.s3.amazonaws.com/28616219/267083420-422a3714-627e-4bef-9fbc-141a2a8ca916.png)
19+
20+
你按需填写相应的配置项:
21+
22+
- **APIKey**:你在 Azure OpenAI 账户页面申请的 API 密钥,可在“密钥和终结点”部分中找到此值
23+
- **API 地址**:Azure API 地址,从 Azure 门户检查资源时,可在“密钥和终结点”部分中找到此值
24+
- **Azure Api Version**: Azure 的 API 版本,遵循 YYYY-MM-DD 格式,查阅[最新版本][azure-api-verion-url]
25+
26+
完成上述字段配置后,点击「检查」,如果提示「检查通过」,则说明配置成功。
27+
28+
## 在部署时配置
29+
30+
如果你希望部署的版本直接配置好 Azure OpenAI,让终端用户直接使用,那么你需要在部署时配置以下环境变量:
31+
32+
| 环境变量 | 类型 | 描述 | 默认值 | 示例 |
33+
| ------------------- | ---- | ------------------------------------------------------------------------- | ------------------ | -------------------------------------------------- |
34+
| `USE_AZURE_OPENAI` | 必选 | 设置改值为 `1` 开启 Azure OpenAI 配置 | - | `1` |
35+
| `AZURE_API_KEY` | 必选 | 这是你在 Azure OpenAI 账户页面申请的 API 密钥 | - | `c55168be3874490ef0565d9779ecd5a6` |
36+
| `OPENAI_PROXY_URL` | 必选 | Azure API 地址,从 Azure 门户检查资源时,可在“密钥和终结点”部分中找到此值 | - | `https://docs-test-001.openai.azure.com` |
37+
| `AZURE_API_VERSION` | 可选 | Azure 的 API 版本,遵循 YYYY-MM-DD 格式 | 2023-08-01-preview | `2023-05-15`,查阅[最新版本][azure-api-verion-url] |
38+
| `ACCESS_CODE` | 可选 | 添加访问此服务的密码,密码应为 6 位数字或字母 | - | `awCT74``e3@09!` |
39+
40+
> **Note**\
41+
> 当你在服务端开启 `USE_AZURE_OPENAI` 后,用户将无法在前端配置中修改并使用 OpenAI key。
42+
43+
[azure-api-verion-url]: https://learn.microsoft.com/zh-cn/azure/ai-services/openai/reference#chat-completions
44+
[azure-openai-url]: https://learn.microsoft.com/zh-cn/azure/ai-services/openai/concepts/models
45+
[rfc]: https://github.com/lobehub/lobe-chat/discussions/178

next.config.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ const nextConfig = {
1414
reactStrictMode: true,
1515
pageExtensions: ['page.tsx', 'api.ts'],
1616
transpilePackages: ['@lobehub/ui'],
17-
17+
env: {
18+
USE_AZURE_OPENAI: process.env.USE_AZURE_OPENAI === '1',
19+
},
1820
webpack(config) {
1921
config.experiments = {
2022
asyncWebAssembly: true,

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@
6565
"dependencies": {
6666
"@ant-design/colors": "^7",
6767
"@ant-design/icons": "^5",
68-
"@azure/openai": "latest",
6968
"@emoji-mart/data": "^1",
7069
"@emoji-mart/react": "^1",
7170
"@icons-pack/react-simple-icons": "^9",

src/config/client.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
declare global {
2+
// eslint-disable-next-line @typescript-eslint/no-namespace
3+
namespace NodeJS {
4+
interface ProcessEnv {
5+
USE_AZURE_OPENAI?: boolean;
6+
}
7+
}
8+
}
9+
10+
export const getClientConfig = () => {
11+
return {
12+
USE_AZURE_OPENAI: process.env.USE_AZURE_OPENAI,
13+
};
14+
};

src/config/server.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ declare global {
44
interface ProcessEnv {
55
ACCESS_CODE?: string;
66
AZURE_API_KEY?: string;
7+
AZURE_API_VERSION?: string;
78
OPENAI_API_KEY?: string;
89
OPENAI_PROXY_URL?: string;
910
}
@@ -17,7 +18,11 @@ export const getServerConfig = () => {
1718

1819
return {
1920
ACCESS_CODE: process.env.ACCESS_CODE,
21+
/* eslint-disable sort-keys-fix/sort-keys-fix */
2022
AZURE_API_KEY: process.env.AZURE_API_KEY,
23+
AZURE_API_VERSION: process.env.AZURE_API_VERSION,
24+
/* eslint-enabled */
25+
2126
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
2227
OPENAI_PROXY_URL: process.env.OPENAI_PROXY_URL,
2328
};

src/const/settings.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getClientConfig } from '@/config/client';
12
import { DEFAULT_OPENAI_MODEL_LIST } from '@/const/llm';
23
import { DEFAULT_AGENT_META } from '@/const/meta';
34
import { LanguageModel } from '@/types/llm';
@@ -36,8 +37,8 @@ export const DEFAULT_AGENT_CONFIG: LobeAgentConfig = {
3637
export const DEFAULT_LLM_CONFIG: GlobalLLMConfig = {
3738
openAI: {
3839
OPENAI_API_KEY: '',
39-
azureApiVersion: '2023-08-01-preview',
4040
models: DEFAULT_OPENAI_MODEL_LIST,
41+
useAzure: getClientConfig().USE_AZURE_OPENAI,
4142
},
4243
};
4344

src/locales/default/setting.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export default {
7373
useAzure: {
7474
desc: '使用 Azure 提供的 OpenAI 服务',
7575
fetch: '获取列表',
76+
serverConfig: '管理员在服务端配置开启了 Azure OpenAI,禁止切换',
7677
title: 'Azure OpenAI',
7778
},
7879
},

src/pages/api/openai/chat.api.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import OpenAI from 'openai';
22

3+
import { getClientConfig } from '@/config/client';
34
import { getOpenAIAuthFromRequest } from '@/const/fetch';
4-
import { ChatErrorType, ErrorType } from '@/types/fetch';
5+
import { ErrorType } from '@/types/fetch';
56
import { OpenAIStreamPayload } from '@/types/openai';
67

78
import { checkAuth } from '../auth';
@@ -24,17 +25,12 @@ export default async function handler(req: Request) {
2425
}
2526

2627
let openai: OpenAI;
27-
if (useAzure) {
28-
if (!apiVersion) return createErrorResponse(ChatErrorType.BadRequest);
2928

30-
// `https://test-001.openai.azure.com/openai/deployments/gpt-35-turbo`,
31-
const url = `${endpoint}/openai/deployments/${payload.model.replace('.', '')}`;
29+
const { USE_AZURE_OPENAI } = getClientConfig();
30+
const useAzureOpenAI = useAzure || USE_AZURE_OPENAI;
3231

33-
openai = createAzureOpenai({
34-
apiVersion,
35-
endpoint: url,
36-
userApiKey: apiKey,
37-
});
32+
if (useAzureOpenAI) {
33+
openai = createAzureOpenai({ apiVersion, endpoint, model: payload.model, userApiKey: apiKey });
3834
} else {
3935
openai = createOpenai(apiKey, endpoint);
4036
}

src/pages/api/openai/createAzureOpenai.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
import OpenAI, { ClientOptions } from 'openai';
2+
import urlJoin from 'url-join';
23

34
import { getServerConfig } from '@/config/server';
45

56
// 创建 Azure OpenAI 实例
67
export const createAzureOpenai = (params: {
7-
apiVersion: string;
8-
endpoint: string;
8+
apiVersion?: string | null;
9+
endpoint?: string | null;
10+
model: string;
911
userApiKey?: string | null;
1012
}) => {
11-
const { AZURE_API_KEY } = getServerConfig();
13+
const { OPENAI_PROXY_URL = '', AZURE_API_VERSION, AZURE_API_KEY } = getServerConfig();
1214

13-
const baseURL = params.endpoint;
14-
const apiKey = !params.userApiKey ? AZURE_API_KEY : params.userApiKey;
15+
const endpoint = !params.endpoint ? OPENAI_PROXY_URL : params.endpoint;
16+
const baseURL = urlJoin(endpoint, `/openai/deployments/${params.model.replace('.', '')}`); // refs: https://test-001.openai.azure.com/openai/deployments/gpt-35-turbo
17+
18+
const defaultApiVersion = AZURE_API_VERSION || '2023-08-01-preview';
19+
const apiVersion = !params.apiVersion ? defaultApiVersion : params.apiVersion;
20+
const apiKey = !params.userApiKey ? AZURE_API_KEY ?? '' : params.userApiKey;
1521

1622
const config: ClientOptions = {
17-
apiKey: apiKey,
23+
apiKey,
1824
baseURL,
1925
defaultHeaders: { 'api-key': apiKey },
20-
defaultQuery: {
21-
'api-version': params.apiVersion,
22-
},
26+
defaultQuery: { 'api-version': apiVersion },
2327
};
2428

2529
return new OpenAI(config);

src/pages/settings/features/Settings/LLM/index.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { memo } from 'react';
88
import { Trans, useTranslation } from 'react-i18next';
99
import { Flexbox } from 'react-layout-kit';
1010

11+
import { getClientConfig } from '@/config/client';
1112
import { FORM_STYLE } from '@/const/layoutTokens';
1213
import { globalSelectors, useEffectAfterGlobalHydrated, useGlobalStore } from '@/store/global';
1314

@@ -81,7 +82,17 @@ const LLM = memo(() => {
8182
name: [configKey, 'openAI', 'endpoint'],
8283
},
8384
{
84-
children: <Switch />,
85+
children: (
86+
<Switch disabled={getClientConfig().USE_AZURE_OPENAI} />
87+
// <Flexbox gap={4}>
88+
// <div>
89+
//
90+
// </div>
91+
// {getClientConfig().USE_AZURE_OPENAI && (
92+
// <div className={styles.tip}>{t('llm.OpenAI.useAzure.serverConfig')}</div>
93+
// )}
94+
// </Flexbox>
95+
),
8596
desc: t('llm.OpenAI.useAzure.desc'),
8697
label: t('llm.OpenAI.useAzure.title'),
8798
name: [configKey, 'openAI', 'useAzure'],

0 commit comments

Comments
 (0)