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

Add Azure OpenAI API support. #935

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1994990
feat: 增加Azure opanai 能力支持
realDuang Apr 19, 2023
ffe4c91
feat: support multi-lang.
realDuang Apr 19, 2023
45e077c
fix: fix bugs when choosing azure api version.
realDuang Apr 19, 2023
3e904f3
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web in…
realDuang Apr 25, 2023
cc9fd7c
feat: change the text of the private version.
realDuang May 9, 2023
435452d
feat: support azure openai API.
realDuang May 9, 2023
0c4be68
Revert "feat: change the text of the private version."
realDuang May 9, 2023
6cd1dbc
feat: support azure openai custom subdomain name config.
realDuang May 9, 2023
bc05057
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
actions-user May 10, 2023
5802365
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
actions-user May 11, 2023
a559021
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
realDuang May 17, 2023
d43bb20
fix: fix merge conflict.
realDuang May 17, 2023
3e5c382
chore: change the owner.
realDuang May 17, 2023
4fc0d20
Merge branch 'Yidadaa:main' into main
realDuang May 17, 2023
1c5e85a
chore: support cs language.
realDuang May 17, 2023
7244a22
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web in…
realDuang May 24, 2023
5015709
fix: fix the wrong domain request when using azure api
realDuang May 24, 2023
39f6d85
fix: disable usage check when aoai enabled.
realDuang May 24, 2023
293db13
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
realDuang May 24, 2023
5977780
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
actions-user May 25, 2023
2c41815
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
actions-user May 26, 2023
27b8ef2
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
actions-user May 27, 2023
2034e03
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
actions-user May 30, 2023
36452a9
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
actions-user Jun 1, 2023
87c84e9
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web in…
realDuang Jun 1, 2023
f890741
Block model config UI when using Azure OpenAI
LaChimere Jun 1, 2023
b4da155
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
actions-user Jun 2, 2023
f3efba3
Merge pull request #2 from LaChimere/lachimere/azure_openai
realDuang Jun 2, 2023
8f0d3dc
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
actions-user Jun 3, 2023
3807c93
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
actions-user Jun 6, 2023
979ed65
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
realDuang Jun 29, 2023
953c87b
Merge branch 'duang/azure_openai'
realDuang Jun 29, 2023
b30e477
fix: deal some conflict in web endpoint
realDuang Jun 29, 2023
7d0ccbd
Merge branch 'duang/azure_openai' of github.com:realDuang/ChatGPT-Nex…
realDuang Jun 29, 2023
44135a9
chore: revert author change
realDuang Jun 29, 2023
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
7 changes: 7 additions & 0 deletions app/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ function parseApiKey(bearToken: string) {

export function auth(req: NextRequest) {
const authToken = req.headers.get("Authorization") ?? "";
const aoaiApiKey = req.headers.get("azure-api-key") ?? "";
realDuang marked this conversation as resolved.
Show resolved Hide resolved

if (!!aoaiApiKey) {
return {
error: false,
};
}

// check if it is openai api key or user token
const { accessCode, apiKey: token } = parseApiKey(authToken);
Expand Down
4 changes: 4 additions & 0 deletions app/client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,9 @@ export function getHeaders() {
);
}

if (accessStore.enableAOAI && validString(accessStore.aoaiToken)) {
headers["api-key"] = accessStore.aoaiToken;
}

return headers;
}
27 changes: 25 additions & 2 deletions app/client/platforms/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import {
OpenaiPath,
REQUEST_TIMEOUT_MS,
} from "@/app/constant";
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
import {
useAccessStore,
useAppConfig,
useChatStore,
AZURE_API_VERSION,
} from "@/app/store";

import { ChatOptions, getHeaders, LLMApi, LLMUsage } from "../api";
import Locale from "../../locales";
Expand All @@ -14,11 +19,29 @@ import {
import { prettyObject } from "@/app/utils/format";

export class ChatGPTApi implements LLMApi {
public get ChatPath() {
const OPENAI_REQUEST_PATH = OpenaiPath.ChatPath;
const { enableAOAI, azureDeployName } = useAccessStore.getState();
if (!enableAOAI) return OPENAI_REQUEST_PATH;

// For now azure api only support one version
const azureApiVersion = AZURE_API_VERSION[0].name;

const AZURE_REQUEST_PATH = `openai/deployments/${azureDeployName}/chat/completions?api-version=${azureApiVersion}`;
return AZURE_REQUEST_PATH;
}

path(path: string): string {
let openaiUrl = useAccessStore.getState().openaiUrl;
if (openaiUrl.length === 0) {
openaiUrl = DEFAULT_API_HOST;
}

const { enableAOAI, azureEndpoint } = useAccessStore.getState();
if (enableAOAI) {
openaiUrl = azureEndpoint;
}

if (openaiUrl.endsWith("/")) {
openaiUrl = openaiUrl.slice(0, openaiUrl.length - 1);
}
Expand Down Expand Up @@ -59,7 +82,7 @@ export class ChatGPTApi implements LLMApi {
options.onController?.(controller);

try {
const chatPath = this.path(OpenaiPath.ChatPath);
const chatPath = this.path(this.ChatPath);
const chatPayload = {
method: "POST",
body: JSON.stringify(requestPayload),
Expand Down
23 changes: 21 additions & 2 deletions app/components/mask.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ import EyeIcon from "../icons/eye.svg";
import CopyIcon from "../icons/copy.svg";

import { DEFAULT_MASK_AVATAR, Mask, useMaskStore } from "../store/mask";
import { ChatMessage, ModelConfig, useAppConfig, useChatStore } from "../store";
import {
ChatMessage,
ModelConfig,
useAccessStore,
useAppConfig,
useChatStore,
} from "../store";
import { ROLES } from "../client/api";
import {
Input,
Expand All @@ -29,7 +35,7 @@ import Locale, { AllLangs, ALL_LANG_OPTIONS, Lang } from "../locales";
import { useNavigate } from "react-router-dom";

import chatStyle from "./chat.module.scss";
import { useEffect, useState } from "react";
import { useState } from "react";
import { downloadAs, readFromFile } from "../utils";
import { Updater } from "../typing";
import { ModelConfigList } from "./model-config";
Expand Down Expand Up @@ -67,6 +73,8 @@ export function MaskConfig(props: {

const globalConfig = useAppConfig();

const accessStore = useAccessStore();

return (
<>
<ContextPrompts
Expand Down Expand Up @@ -149,6 +157,17 @@ export function MaskConfig(props: {
) : null}
</List>

{accessStore.enableAOAI ? (
<List>
<ListItem
title={Locale.Settings.AzureDeploymentName.Title}
subTitle={Locale.Settings.AzureDeploymentName.SubTitle}
>
<input value={accessStore.azureDeployName} />
</ListItem>
</List>
) : null}

<List>
<ModelConfigList
modelConfig={{ ...props.mask.modelConfig }}
Expand Down
50 changes: 30 additions & 20 deletions app/components/model-config.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { ALL_MODELS, ModalConfigValidator, ModelConfig } from "../store";
import {
ALL_MODELS,
ModalConfigValidator,
ModelConfig,
useAccessStore,
} from "../store";

import Locale from "../locales";
import { InputRange } from "./input-range";
Expand All @@ -8,27 +13,32 @@ export function ModelConfigList(props: {
modelConfig: ModelConfig;
updateConfig: (updater: (config: ModelConfig) => void) => void;
}) {
const accessStore = useAccessStore();

return (
<>
<ListItem title={Locale.Settings.Model}>
<Select
value={props.modelConfig.model}
onChange={(e) => {
props.updateConfig(
(config) =>
(config.model = ModalConfigValidator.model(
e.currentTarget.value,
)),
);
}}
>
{ALL_MODELS.map((v) => (
<option value={v.name} key={v.name} disabled={!v.available}>
{v.name}
</option>
))}
</Select>
</ListItem>
{accessStore.enableAOAI ? null : (
<ListItem title={Locale.Settings.Model}>
<Select
value={props.modelConfig.model}
onChange={(e) => {
props.updateConfig(
(config) =>
(config.model = ModalConfigValidator.model(
e.currentTarget.value,
)),
);
}}
>
{ALL_MODELS.map((v) => (
<option value={v.name} key={v.name} disabled={!v.available}>
{v.name}
</option>
))}
</Select>
</ListItem>
)}

<ListItem
title={Locale.Settings.Temperature.Title}
subTitle={Locale.Settings.Temperature.SubTitle}
Expand Down
158 changes: 105 additions & 53 deletions app/components/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,8 @@ export function Settings() {
const customCount = promptStore.getUserPrompts().length ?? 0;
const [shouldShowPromptModal, setShowPromptModal] = useState(false);

const showUsage = accessStore.isAuthorized();
// For now azure openai api do not expose usage api
const showUsage = accessStore.isAuthorized() && !accessStore.enableAOAI;
useEffect(() => {
// checks per minutes
checkUpdate();
Expand Down Expand Up @@ -594,62 +595,113 @@ export function Settings() {
<></>
)}

{!accessStore.hideUserApiKey ? (
<ListItem
title={Locale.Settings.Token.Title}
subTitle={Locale.Settings.Token.SubTitle}
>
<PasswordInput
value={accessStore.token}
type="text"
placeholder={Locale.Settings.Token.Placeholder}
onChange={(e) => {
accessStore.updateToken(e.currentTarget.value);
}}
/>
</ListItem>
) : null}
<ListItem title={Locale.Settings.EnableAOAI}>
<input
type="checkbox"
checked={accessStore.enableAOAI}
onChange={(e) => {
accessStore.switchAOAI(e.currentTarget.checked);
}}
></input>
</ListItem>

{!accessStore.hideBalanceQuery ? (
<ListItem
title={Locale.Settings.Usage.Title}
subTitle={
showUsage
? loadingUsage
? Locale.Settings.Usage.IsChecking
: Locale.Settings.Usage.SubTitle(
usage?.used ?? "[?]",
usage?.subscription ?? "[?]",
)
: Locale.Settings.Usage.NoAccess
}
>
{!showUsage || loadingUsage ? (
<div />
) : (
<IconButton
icon={<ResetIcon></ResetIcon>}
text={Locale.Settings.Usage.Check}
onClick={() => checkUsage(true)}
{accessStore.enableAOAI ? (
<>
<ListItem
title={Locale.Settings.AzureDeploymentName.Title}
subTitle={Locale.Settings.AzureDeploymentName.SubTitle}
>
<input
value={accessStore.azureDeployName}
type="text"
placeholder={Locale.Settings.AzureDeploymentName.Placeholder}
onChange={(e) => {
accessStore.updateAzureDeployName(e.currentTarget.value);
}}
/>
)}
</ListItem>
</ListItem>
<ListItem title={Locale.Settings.AOAIToken.Title}>
<PasswordInput
value={accessStore.aoaiToken}
type="text"
placeholder={Locale.Settings.AOAIToken.Placeholder}
onChange={(e) => {
accessStore.updateAOAIToken(e.currentTarget.value);
}}
/>
</ListItem>
<ListItem title={Locale.Settings.AzureEndpoint.Title}>
<input
value={accessStore.azureEndpoint}
type="text"
placeholder={Locale.Settings.AzureEndpoint.Placeholder}
onChange={(e) => {
accessStore.updateAzureEndpoint(e.currentTarget.value);
}}
/>
</ListItem>
</>
) : null}

{!accessStore.hideUserApiKey ? (
<ListItem
title={Locale.Settings.Endpoint.Title}
subTitle={Locale.Settings.Endpoint.SubTitle}
>
<input
type="text"
value={accessStore.openaiUrl}
placeholder="https://api.openai.com/"
onChange={(e) =>
accessStore.updateOpenAiUrl(e.currentTarget.value)
}
></input>
</ListItem>
{!accessStore.enableAOAI ? (
<>
{!accessStore.hideUserApiKey ? (
<ListItem
title={Locale.Settings.Token.Title}
subTitle={Locale.Settings.Token.SubTitle}
>
<PasswordInput
value={accessStore.token}
type="text"
placeholder={Locale.Settings.Token.Placeholder}
onChange={(e) => {
accessStore.updateToken(e.currentTarget.value);
}}
/>
</ListItem>
) : null}

{!accessStore.hideBalanceQuery ? (
<ListItem
title={Locale.Settings.Usage.Title}
subTitle={
showUsage
? loadingUsage
? Locale.Settings.Usage.IsChecking
: Locale.Settings.Usage.SubTitle(
usage?.used ?? "[?]",
usage?.subscription ?? "[?]",
)
: Locale.Settings.Usage.NoAccess
}
>
{!showUsage || loadingUsage ? (
<div />
) : (
<IconButton
icon={<ResetIcon></ResetIcon>}
text={Locale.Settings.Usage.Check}
onClick={() => checkUsage(true)}
/>
)}
</ListItem>
) : null}
{!accessStore.hideUserApiKey ? (
<ListItem
title={Locale.Settings.Endpoint.Title}
subTitle={Locale.Settings.Endpoint.SubTitle}
>
<input
type="text"
value={accessStore.openaiUrl}
placeholder="https://api.openai.com/"
onChange={(e) =>
accessStore.updateOpenAiUrl(e.currentTarget.value)
}
></input>
</ListItem>
) : null}
</>
) : null}
</List>

Expand Down
15 changes: 15 additions & 0 deletions app/locales/cn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ const cn = {
Title: "面具启动页",
SubTitle: "新建聊天时,展示面具启动页",
},

Prompt: {
Disable: {
Title: "禁用提示词自动补全",
Expand Down Expand Up @@ -191,6 +192,20 @@ const cn = {
Placeholder: "OpenAI API Key",
},

EnableAOAI: "使用 Azure OpenAI",
AzureEndpoint: {
Title: "Azure OpenAI Endpoint",
Placeholder: "https://docs-test-001.openai.azure.com/",
},
AzureDeploymentName: {
Title: "Azure OpenAI 部署实例名称",
SubTitle: "此值将对应于在部署模型时为部署选择的自定义名称",
Placeholder: "部署自定义名称",
},
AOAIToken: {
Title: "Azure OpenAI API Key",
Placeholder: "Azure OpenAI API Key",
},
Usage: {
Title: "余额查询",
SubTitle(used: any, total: any) {
Expand Down
Loading