Skip to content

Changes needed to support Azure OpenAI Proxy usage #49

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

Merged
merged 3 commits into from
Jun 19, 2024
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
4 changes: 2 additions & 2 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ OPENAI_EMBED_HOST=azure
# You also need to `azd auth login` if running this locally
AZURE_OPENAI_ENDPOINT=https://YOUR-AZURE-OPENAI-SERVICE-NAME.openai.azure.com
AZURE_OPENAI_VERSION=2024-03-01-preview
AZURE_OPENAI_CHAT_DEPLOYMENT=chat
AZURE_OPENAI_CHAT_DEPLOYMENT=gpt-35-turbo
AZURE_OPENAI_CHAT_MODEL=gpt-35-turbo
AZURE_OPENAI_EMBED_DEPLOYMENT=embed
AZURE_OPENAI_EMBED_DEPLOYMENT=text-embedding-ada-002
AZURE_OPENAI_EMBED_MODEL=text-embedding-ada-002
AZURE_OPENAI_EMBED_MODEL_DIMENSIONS=1536
# Needed for OpenAI.com:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ Since the local app uses OpenAI models, you should first deploy it for the optim

There must be an initial build of static assets before running the backend, since the backend serves static files from the `src/static` directory.

3. Run the FastAPI backend (with hot reloading):
3. Run the FastAPI backend (with hot reloading). This should be run from the root of the project:

```shell
python3 -m uvicorn fastapi_app:create_app --factory --reload
Expand Down
210 changes: 115 additions & 95 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ param principalId string = ''
param openAILocation string

@description('Name of the OpenAI resource group. If not specified, the resource group name will be generated.')
param openAiResourceGroupName string = ''
param openAIResourceGroupName string = ''

@description('Whether to deploy Azure OpenAI resources')
param deployAzureOpenAI bool = true
Expand All @@ -47,15 +47,22 @@ param chatDeploymentName string = ''
// https://learn.microsoft.com/azure/ai-services/openai/concepts/models#gpt-4-and-gpt-4-turbo-preview-models
param chatDeploymentVersion string = ''

param azureOpenAiAPIVersion string = '2024-03-01-preview'
param azureOpenAIAPIVersion string = '2024-03-01-preview'
@secure()
param azureOpenAIKey string = ''
@description('Azure OpenAI endpoint to use, if not using the one deployed here.')
param azureOpenAIEndpoint string = ''

@description('Whether to use Azure OpenAI (either deployed here or elsewhere) or OpenAI.com')
var useAzureOpenAI = deployAzureOpenAI || !empty(azureOpenAIEndpoint)

@description('Capacity of the GPT deployment')
// You can increase this, but capacity is limited per model/region, so you will get errors if you go over
// https://learn.microsoft.com/en-us/azure/ai-services/openai/quotas-limits
param chatDeploymentCapacity int = 0
var chatConfig = {
modelName: !empty(chatModelName) ? chatModelName : deployAzureOpenAI ? 'gpt-35-turbo' : 'gpt-3.5-turbo'
deploymentName: !empty(chatDeploymentName) ? chatDeploymentName : 'chat'
modelName: !empty(chatModelName) ? chatModelName : (useAzureOpenAI ? 'gpt-35-turbo' : 'gpt-3.5-turbo')
deploymentName: !empty(chatDeploymentName) ? chatDeploymentName : 'gpt-35-turbo'
deploymentVersion: !empty(chatDeploymentVersion) ? chatDeploymentVersion : '0125'
deploymentCapacity: chatDeploymentCapacity != 0 ? chatDeploymentCapacity : 30
}
Expand All @@ -68,7 +75,7 @@ param embedDimensions int = 0

var embedConfig = {
modelName: !empty(embedModelName) ? embedModelName : 'text-embedding-ada-002'
deploymentName: !empty(embedDeploymentName) ? embedDeploymentName : 'embed'
deploymentName: !empty(embedDeploymentName) ? embedDeploymentName : 'text-embedding-ada-002'
deploymentVersion: !empty(embedDeploymentVersion) ? embedDeploymentVersion : '2'
deploymentCapacity: embedDeploymentCapacity != 0 ? embedDeploymentCapacity : 30
dimensions: embedDimensions != 0 ? embedDimensions : 1536
Expand Down Expand Up @@ -145,6 +152,91 @@ module containerApps 'core/host/container-apps.bicep' = {
// Web frontend
var webAppName = replace('${take(prefix, 19)}-ca', '--', '-')
var webAppIdentityName = '${prefix}-id-web'
var webAppEnv = [
{
name: 'POSTGRES_HOST'
value: postgresServer.outputs.POSTGRES_DOMAIN_NAME
}
{
name: 'POSTGRES_USERNAME'
value: webAppIdentityName
}
{
name: 'POSTGRES_DATABASE'
value: postgresDatabaseName
}
{
name: 'POSTGRES_SSL'
value: 'require'
}
{
name: 'RUNNING_IN_PRODUCTION'
value: 'true'
}
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: monitoring.outputs.applicationInsightsConnectionString
}
{
name: 'OPENAI_CHAT_HOST'
value: useAzureOpenAI ? 'azure' : 'openaicom'
}
{
name: 'AZURE_OPENAI_CHAT_DEPLOYMENT'
value: useAzureOpenAI ? chatConfig.deploymentName : ''
}
{
name: 'AZURE_OPENAI_CHAT_MODEL'
value: useAzureOpenAI ? chatConfig.modelName : ''
}
{
name: 'OPENAICOM_CHAT_MODEL'
value: useAzureOpenAI ? '' : 'gpt-3.5-turbo'
}
{
name: 'OPENAI_EMBED_HOST'
value: useAzureOpenAI ? 'azure' : 'openaicom'
}
{
name: 'OPENAICOM_EMBED_MODEL_DIMENSIONS'
value: useAzureOpenAI ? '' : '1536'
}
{
name: 'OPENAICOM_EMBED_MODEL'
value: useAzureOpenAI ? '' : 'text-embedding-ada-002'
}
{
name: 'AZURE_OPENAI_EMBED_MODEL'
value: useAzureOpenAI ? embedConfig.modelName : ''
}
{
name: 'AZURE_OPENAI_EMBED_DEPLOYMENT'
value: useAzureOpenAI ? embedConfig.deploymentName : ''
}
{
name: 'AZURE_OPENAI_EMBED_MODEL_DIMENSIONS'
value: useAzureOpenAI ? string(embedConfig.dimensions) : ''
}
{
name: 'AZURE_OPENAI_ENDPOINT'
value: useAzureOpenAI ? (deployAzureOpenAI ? openAI.outputs.endpoint : azureOpenAIEndpoint) : ''
}
{
name: 'AZURE_OPENAI_VERSION'
value: useAzureOpenAI ? azureOpenAIAPIVersion : ''
}
]
var webAppEnvWithSecret = !empty(azureOpenAIKey) ? union(webAppEnv, [
{
name: 'AZURE_OPENAI_KEY'
secretRef: 'azure-openai-key'
}
]) : webAppEnv

var secrets = !empty(azureOpenAIKey) ? {
'azure-openai-key': azureOpenAIKey
} : {}

module web 'web.bicep' = {
name: 'web'
scope: resourceGroup
Expand All @@ -156,91 +248,19 @@ module web 'web.bicep' = {
containerAppsEnvironmentName: containerApps.outputs.environmentName
containerRegistryName: containerApps.outputs.registryName
exists: webAppExists
environmentVariables: [
{
name: 'POSTGRES_HOST'
value: postgresServer.outputs.POSTGRES_DOMAIN_NAME
}
{
name: 'POSTGRES_USERNAME'
value: webAppIdentityName
}
{
name: 'POSTGRES_DATABASE'
value: postgresDatabaseName
}
{
name: 'POSTGRES_SSL'
value: 'require'
}
{
name: 'RUNNING_IN_PRODUCTION'
value: 'true'
}
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: monitoring.outputs.applicationInsightsConnectionString
}
{
name: 'OPENAI_CHAT_HOST'
value: deployAzureOpenAI ? 'azure' : 'openaicom'
}
{
name: 'AZURE_OPENAI_CHAT_DEPLOYMENT'
value: deployAzureOpenAI ? chatConfig.deploymentName : ''
}
{
name: 'AZURE_OPENAI_CHAT_MODEL'
value: deployAzureOpenAI ? chatConfig.modelName : ''
}
{
name: 'OPENAICOM_CHAT_MODEL'
value: deployAzureOpenAI ? '' : 'gpt-3.5-turbo'
}
{
name: 'OPENAI_EMBED_HOST'
value: deployAzureOpenAI ? 'azure' : 'openaicom'
}
{
name: 'OPENAICOM_EMBED_MODEL_DIMENSIONS'
value: deployAzureOpenAI ? '' : '1536'
}
{
name: 'OPENAICOM_EMBED_MODEL'
value: deployAzureOpenAI ? '' : 'text-embedding-ada-002'
}
{
name: 'AZURE_OPENAI_EMBED_MODEL'
value: deployAzureOpenAI ? embedConfig.modelName : ''
}
{
name: 'AZURE_OPENAI_EMBED_DEPLOYMENT'
value: deployAzureOpenAI ? embedConfig.deploymentName : ''
}
{
name: 'AZURE_OPENAI_EMBED_MODEL_DIMENSIONS'
value: deployAzureOpenAI ? string(embedConfig.dimensions) : ''
}
{
name: 'AZURE_OPENAI_ENDPOINT'
value: deployAzureOpenAI ? openAi.outputs.endpoint : ''
}
{
name: 'AZURE_OPENAI_VERSION'
value: deployAzureOpenAI ? azureOpenAiAPIVersion : ''
}
]
environmentVariables: webAppEnvWithSecret
secrets: secrets
}
}

resource openAiResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' existing =
if (!empty(openAiResourceGroupName)) {
name: !empty(openAiResourceGroupName) ? openAiResourceGroupName : resourceGroup.name
resource openAIResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' existing =
if (!empty(openAIResourceGroupName)) {
name: !empty(openAIResourceGroupName) ? openAIResourceGroupName : resourceGroup.name
}

module openAi 'core/ai/cognitiveservices.bicep' = {
module openAI 'core/ai/cognitiveservices.bicep' = if (deployAzureOpenAI) {
name: 'openai'
scope: openAiResourceGroup
scope: openAIResourceGroup
params: {
name: '${prefix}-openai'
location: openAILocation
Expand Down Expand Up @@ -279,9 +299,9 @@ module openAi 'core/ai/cognitiveservices.bicep' = {
}

// USER ROLES
module openAiRoleUser 'core/security/role.bicep' =
module openAIRoleUser 'core/security/role.bicep' =
if (empty(runningOnGh)) {
scope: openAiResourceGroup
scope: openAIResourceGroup
name: 'openai-role-user'
params: {
principalId: principalId
Expand All @@ -291,8 +311,8 @@ module openAiRoleUser 'core/security/role.bicep' =
}

// Backend roles
module openAiRoleBackend 'core/security/role.bicep' = {
scope: openAiResourceGroup
module openAIRoleBackend 'core/security/role.bicep' = {
scope: openAIResourceGroup
name: 'openai-role-backend'
params: {
principalId: web.outputs.SERVICE_WEB_IDENTITY_PRINCIPAL_ID
Expand All @@ -314,13 +334,13 @@ output SERVICE_WEB_NAME string = web.outputs.SERVICE_WEB_NAME
output SERVICE_WEB_URI string = web.outputs.SERVICE_WEB_URI
output SERVICE_WEB_IMAGE_NAME string = web.outputs.SERVICE_WEB_IMAGE_NAME

output AZURE_OPENAI_ENDPOINT string = deployAzureOpenAI ? openAi.outputs.endpoint : ''
output AZURE_OPENAI_VERSION string = deployAzureOpenAI ? azureOpenAiAPIVersion : ''
output AZURE_OPENAI_CHAT_DEPLOYMENT string = deployAzureOpenAI ? chatConfig.deploymentName : ''
output AZURE_OPENAI_EMBED_DEPLOYMENT string = deployAzureOpenAI ? embedConfig.deploymentName : ''
output AZURE_OPENAI_CHAT_MODEL string = deployAzureOpenAI ? chatConfig.modelName : ''
output AZURE_OPENAI_EMBED_MODEL string = deployAzureOpenAI ? embedConfig.modelName : ''
output AZURE_OPENAI_EMBED_MODEL_DIMENSIONS int = deployAzureOpenAI ? embedConfig.dimensions : 0
output AZURE_OPENAI_ENDPOINT string = useAzureOpenAI ? (deployAzureOpenAI ? openAI.outputs.endpoint : azureOpenAIEndpoint) : ''
output AZURE_OPENAI_VERSION string = useAzureOpenAI ? azureOpenAIAPIVersion : ''
output AZURE_OPENAI_CHAT_DEPLOYMENT string = useAzureOpenAI ? chatConfig.deploymentName : ''
output AZURE_OPENAI_EMBED_DEPLOYMENT string = useAzureOpenAI ? embedConfig.deploymentName : ''
output AZURE_OPENAI_CHAT_MODEL string = useAzureOpenAI ? chatConfig.modelName : ''
output AZURE_OPENAI_EMBED_MODEL string = useAzureOpenAI ? embedConfig.modelName : ''
output AZURE_OPENAI_EMBED_MODEL_DIMENSIONS int = useAzureOpenAI ? embedConfig.dimensions : 0

output POSTGRES_HOST string = postgresServer.outputs.POSTGRES_DOMAIN_NAME
output POSTGRES_USERNAME string = postgresEntraAdministratorName
Expand Down
6 changes: 6 additions & 0 deletions infra/main.parameters.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
"deployAzureOpenAI": {
"value": "${DEPLOY_AZURE_OPENAI=true}"
},
"azureOpenAIKey": {
"value": "${AZURE_OPENAI_KEY}"
},
"azureOpenAIEndpoint": {
"value": "${AZURE_OPENAI_ENDPOINT}"
},
"chatModelName":{
"value": "${AZURE_OPENAI_CHAT_MODEL}"
},
Expand Down
3 changes: 3 additions & 0 deletions infra/web.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ param exists bool
param identityName string
param serviceName string = 'web'
param environmentVariables array = []
@secure()
param secrets object

resource webIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: identityName
Expand Down Expand Up @@ -37,6 +39,7 @@ module app 'core/host/container-app-upsert.bicep' = {
}
]
)
secrets: secrets
targetPort: 8000
}
}
Expand Down
32 changes: 22 additions & 10 deletions src/fastapi_app/openai_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,21 @@
async def create_openai_chat_client(azure_credential):
OPENAI_CHAT_HOST = os.getenv("OPENAI_CHAT_HOST")
if OPENAI_CHAT_HOST == "azure":
logger.info("Authenticating to OpenAI using Azure Identity...")

token_provider = azure.identity.get_bearer_token_provider(
azure_credential, "https://cognitiveservices.azure.com/.default"
)
client_args = {}
if api_key := os.getenv("AZURE_OPENAI_KEY"):
logger.info("Authenticating to Azure OpenAI using API key...")
client_args["api_key"] = api_key
else:
logger.info("Authenticating to Azure OpenAI using Azure Identity...")
token_provider = azure.identity.get_bearer_token_provider(
azure_credential, "https://cognitiveservices.azure.com/.default"
)
client_args["azure_ad_token_provider"] = token_provider
openai_chat_client = openai.AsyncAzureOpenAI(
api_version=os.getenv("AZURE_OPENAI_VERSION"),
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
azure_ad_token_provider=token_provider,
azure_deployment=os.getenv("AZURE_OPENAI_CHAT_DEPLOYMENT"),
**client_args,
)
openai_chat_model = os.getenv("AZURE_OPENAI_CHAT_MODEL")
elif OPENAI_CHAT_HOST == "ollama":
Expand All @@ -40,14 +45,21 @@ async def create_openai_chat_client(azure_credential):
async def create_openai_embed_client(azure_credential):
OPENAI_EMBED_HOST = os.getenv("OPENAI_EMBED_HOST")
if OPENAI_EMBED_HOST == "azure":
token_provider = azure.identity.get_bearer_token_provider(
azure_credential, "https://cognitiveservices.azure.com/.default"
)
client_args = {}
if api_key := os.getenv("AZURE_OPENAI_KEY"):
logger.info("Authenticating to Azure OpenAI using API key...")
client_args["api_key"] = api_key
else:
logger.info("Authenticating to Azure OpenAI using Azure Identity...")
token_provider = azure.identity.get_bearer_token_provider(
azure_credential, "https://cognitiveservices.azure.com/.default"
)
client_args["azure_ad_token_provider"] = token_provider
openai_embed_client = openai.AsyncAzureOpenAI(
api_version=os.getenv("AZURE_OPENAI_VERSION"),
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
azure_ad_token_provider=token_provider,
azure_deployment=os.getenv("AZURE_OPENAI_EMBED_DEPLOYMENT"),
**client_args,
)
openai_embed_model = os.getenv("AZURE_OPENAI_EMBED_MODEL")
openai_embed_dimensions = os.getenv("AZURE_OPENAI_EMBED_DIMENSIONS")
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GPT + Enterprise data | Sample</title>
<title>RAG on PostgreSQL</title>
</head>
<body>
<div id="root"></div>
Expand Down
Loading