Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feat: Support Azure OpenAI Deploy env #183

Merged
merged 4 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions docs/deploy-with-azure-openai.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# 使用 Azure OpenAI 部署

LobeChat 支持使用 [Azure OpenAI][azure-openai-url] 作为 OpenAI 的模型服务商,本文将介绍如何配置 Azure OpenAI。

## 使用限制

从研发成本考虑([#178][rfc]),目前阶段的 LobeChat 并没有 100% 完全符合 Azure OpenAI 的实现模型,采用了以 `openai` 为基座,兼容 Azure OpeAI 的解决方案。因此会带来以下局限性:

- OpenAI 与 Azure OpenAI 只能二选一,当你开启使用 Azure OpenAI 后,将无法使用 OpenAI 作为模型服务商;
- LobeChat 约定了与模型同名的部署名才能正常使用,比如 `gpt-35-turbo` 模型的部署名,必须为 `gpt-35-turbo`,否则 LobeChat 将无法正常正确匹配到相应模型
![](https://github-production-user-asset-6210df.s3.amazonaws.com/28616219/267082091-d89d53d3-1c8c-40ca-ba15-0a9af2a79264.png)
- 由于 Azure OpenAI 的 SDK 接入复杂度,当前无法查询配置资源的模型列表;

## 在界面中配置

点击左下角「操作」 -「设置」,切到 「语言模型」 Tab 后通过开启「Azure OpenAI」开关,即可开启使用 Azure OpenAI。

![](https://github-production-user-asset-6210df.s3.amazonaws.com/28616219/267083420-422a3714-627e-4bef-9fbc-141a2a8ca916.png)

你按需填写相应的配置项:

- **APIKey**:你在 Azure OpenAI 账户页面申请的 API 密钥,可在“密钥和终结点”部分中找到此值
- **API 地址**:Azure API 地址,从 Azure 门户检查资源时,可在“密钥和终结点”部分中找到此值
- **Azure Api Version**: Azure 的 API 版本,遵循 YYYY-MM-DD 格式,查阅[最新版本][azure-api-verion-url]

完成上述字段配置后,点击「检查」,如果提示「检查通过」,则说明配置成功。

## 在部署时配置

如果你希望部署的版本直接配置好 Azure OpenAI,让终端用户直接使用,那么你需要在部署时配置以下环境变量:

| 环境变量 | 类型 | 描述 | 默认值 | 示例 |
| ------------------- | ---- | ------------------------------------------------------------------------- | ------------------ | -------------------------------------------------- |
| `USE_AZURE_OPENAI` | 必选 | 设置改值为 `1` 开启 Azure OpenAI 配置 | - | `1` |
| `AZURE_API_KEY` | 必选 | 这是你在 Azure OpenAI 账户页面申请的 API 密钥 | - | `c55168be3874490ef0565d9779ecd5a6` |
| `OPENAI_PROXY_URL` | 必选 | Azure API 地址,从 Azure 门户检查资源时,可在“密钥和终结点”部分中找到此值 | - | `https://docs-test-001.openai.azure.com` |
| `AZURE_API_VERSION` | 可选 | Azure 的 API 版本,遵循 YYYY-MM-DD 格式 | 2023-08-01-preview | `2023-05-15`,查阅[最新版本][azure-api-verion-url] |
| `ACCESS_CODE` | 可选 | 添加访问此服务的密码,密码应为 6 位数字或字母 | - | `awCT74``e3@09!` |

> **Note**\
> 当你在服务端开启 `USE_AZURE_OPENAI` 后,用户将无法在前端配置中修改并使用 OpenAI key。
[azure-api-verion-url]: https://learn.microsoft.com/zh-cn/azure/ai-services/openai/reference#chat-completions
[azure-openai-url]: https://learn.microsoft.com/zh-cn/azure/ai-services/openai/concepts/models
[rfc]: https://github.com/lobehub/lobe-chat/discussions/178
4 changes: 3 additions & 1 deletion next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ const nextConfig = {
reactStrictMode: true,
pageExtensions: ['page.tsx', 'api.ts'],
transpilePackages: ['@lobehub/ui'],

env: {
USE_AZURE_OPENAI: process.env.USE_AZURE_OPENAI === '1',
},
webpack(config) {
config.experiments = {
asyncWebAssembly: true,
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@
"dependencies": {
"@ant-design/colors": "^7",
"@ant-design/icons": "^5",
"@azure/openai": "latest",
"@emoji-mart/data": "^1",
"@emoji-mart/react": "^1",
"@icons-pack/react-simple-icons": "^9",
Expand Down
14 changes: 14 additions & 0 deletions src/config/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace NodeJS {
interface ProcessEnv {
USE_AZURE_OPENAI?: boolean;
}
}
}

export const getClientConfig = () => {
return {
USE_AZURE_OPENAI: process.env.USE_AZURE_OPENAI,
};
};
5 changes: 5 additions & 0 deletions src/config/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ declare global {
interface ProcessEnv {
ACCESS_CODE?: string;
AZURE_API_KEY?: string;
AZURE_API_VERSION?: string;
OPENAI_API_KEY?: string;
OPENAI_PROXY_URL?: string;
}
Expand All @@ -17,7 +18,11 @@ export const getServerConfig = () => {

return {
ACCESS_CODE: process.env.ACCESS_CODE,
/* eslint-disable sort-keys-fix/sort-keys-fix */
AZURE_API_KEY: process.env.AZURE_API_KEY,
AZURE_API_VERSION: process.env.AZURE_API_VERSION,
/* eslint-enabled */

OPENAI_API_KEY: process.env.OPENAI_API_KEY,
OPENAI_PROXY_URL: process.env.OPENAI_PROXY_URL,
};
Expand Down
3 changes: 2 additions & 1 deletion src/const/settings.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getClientConfig } from '@/config/client';
import { DEFAULT_OPENAI_MODEL_LIST } from '@/const/llm';
import { DEFAULT_AGENT_META } from '@/const/meta';
import { LanguageModel } from '@/types/llm';
Expand Down Expand Up @@ -36,8 +37,8 @@ export const DEFAULT_AGENT_CONFIG: LobeAgentConfig = {
export const DEFAULT_LLM_CONFIG: GlobalLLMConfig = {
openAI: {
OPENAI_API_KEY: '',
azureApiVersion: '2023-08-01-preview',
models: DEFAULT_OPENAI_MODEL_LIST,
useAzure: getClientConfig().USE_AZURE_OPENAI,
},
};

Expand Down
1 change: 1 addition & 0 deletions src/locales/default/setting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export default {
useAzure: {
desc: '使用 Azure 提供的 OpenAI 服务',
fetch: '获取列表',
serverConfig: '管理员在服务端配置开启了 Azure OpenAI,禁止切换',
title: 'Azure OpenAI',
},
},
Expand Down
16 changes: 6 additions & 10 deletions src/pages/api/openai/chat.api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import OpenAI from 'openai';

import { getClientConfig } from '@/config/client';
import { getOpenAIAuthFromRequest } from '@/const/fetch';
import { ChatErrorType, ErrorType } from '@/types/fetch';
import { ErrorType } from '@/types/fetch';
import { OpenAIStreamPayload } from '@/types/openai';

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

let openai: OpenAI;
if (useAzure) {
if (!apiVersion) return createErrorResponse(ChatErrorType.BadRequest);

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

openai = createAzureOpenai({
apiVersion,
endpoint: url,
userApiKey: apiKey,
});
if (useAzureOpenAI) {
openai = createAzureOpenai({ apiVersion, endpoint, model: payload.model, userApiKey: apiKey });
} else {
openai = createOpenai(apiKey, endpoint);
}
Expand Down
22 changes: 13 additions & 9 deletions src/pages/api/openai/createAzureOpenai.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import OpenAI, { ClientOptions } from 'openai';
import urlJoin from 'url-join';

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

// 创建 Azure OpenAI 实例
export const createAzureOpenai = (params: {
apiVersion: string;
endpoint: string;
apiVersion?: string | null;
endpoint?: string | null;
model: string;
userApiKey?: string | null;
}) => {
const { AZURE_API_KEY } = getServerConfig();
const { OPENAI_PROXY_URL = '', AZURE_API_VERSION, AZURE_API_KEY } = getServerConfig();

const baseURL = params.endpoint;
const apiKey = !params.userApiKey ? AZURE_API_KEY : params.userApiKey;
const endpoint = !params.endpoint ? OPENAI_PROXY_URL : params.endpoint;
const baseURL = urlJoin(endpoint, `/openai/deployments/${params.model.replace('.', '')}`); // refs: https://test-001.openai.azure.com/openai/deployments/gpt-35-turbo

const defaultApiVersion = AZURE_API_VERSION || '2023-08-01-preview';
const apiVersion = !params.apiVersion ? defaultApiVersion : params.apiVersion;
const apiKey = !params.userApiKey ? AZURE_API_KEY ?? '' : params.userApiKey;

const config: ClientOptions = {
apiKey: apiKey,
apiKey,
baseURL,
defaultHeaders: { 'api-key': apiKey },
defaultQuery: {
'api-version': params.apiVersion,
},
defaultQuery: { 'api-version': apiVersion },
};

return new OpenAI(config);
Expand Down
13 changes: 12 additions & 1 deletion src/pages/settings/features/Settings/LLM/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { memo } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

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

Expand Down Expand Up @@ -81,7 +82,17 @@ const LLM = memo(() => {
name: [configKey, 'openAI', 'endpoint'],
},
{
children: <Switch />,
children: (
<Switch disabled={getClientConfig().USE_AZURE_OPENAI} />
// <Flexbox gap={4}>
// <div>
//
// </div>
// {getClientConfig().USE_AZURE_OPENAI && (
// <div className={styles.tip}>{t('llm.OpenAI.useAzure.serverConfig')}</div>
// )}
// </Flexbox>
),
desc: t('llm.OpenAI.useAzure.desc'),
label: t('llm.OpenAI.useAzure.title'),
name: [configKey, 'openAI', 'useAzure'],
Expand Down
2 changes: 1 addition & 1 deletion src/services/_header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const createHeaderWithOpenAI = (header?: HeadersInit): HeadersInit => {

if (openai.useAzure) {
Object.assign(result, {
[AZURE_OPENAI_API_VERSION]: openai.azureApiVersion,
[AZURE_OPENAI_API_VERSION]: openai.azureApiVersion || '',
[USE_AZURE_OPENAI]: '1',
});
}
Expand Down
1 change: 0 additions & 1 deletion src/store/global/selectors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ describe('globalSelectors', () => {
languageModel: {
openAI: {
OPENAI_API_KEY: 'openai-api-key',
azureApiVersion: '2023-08-01-preview',
endpoint: 'https://openai-endpoint.com',
models: ['gpt-3.5-turbo'],
},
Expand Down