Skip to content

Commit fbba92b

Browse files
authored
Merge pull request #49 from Azure-Samples/workshop
Changes needed to support Azure OpenAI Proxy usage
2 parents d5b08fc + f06cc32 commit fbba92b

File tree

7 files changed

+150
-109
lines changed

7 files changed

+150
-109
lines changed

.env.sample

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ OPENAI_EMBED_HOST=azure
1313
# You also need to `azd auth login` if running this locally
1414
AZURE_OPENAI_ENDPOINT=https://YOUR-AZURE-OPENAI-SERVICE-NAME.openai.azure.com
1515
AZURE_OPENAI_VERSION=2024-03-01-preview
16-
AZURE_OPENAI_CHAT_DEPLOYMENT=chat
16+
AZURE_OPENAI_CHAT_DEPLOYMENT=gpt-35-turbo
1717
AZURE_OPENAI_CHAT_MODEL=gpt-35-turbo
18-
AZURE_OPENAI_EMBED_DEPLOYMENT=embed
18+
AZURE_OPENAI_EMBED_DEPLOYMENT=text-embedding-ada-002
1919
AZURE_OPENAI_EMBED_MODEL=text-embedding-ada-002
2020
AZURE_OPENAI_EMBED_MODEL_DIMENSIONS=1536
2121
# Needed for OpenAI.com:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ Since the local app uses OpenAI models, you should first deploy it for the optim
145145
146146
There must be an initial build of static assets before running the backend, since the backend serves static files from the `src/static` directory.
147147
148-
3. Run the FastAPI backend (with hot reloading):
148+
3. Run the FastAPI backend (with hot reloading). This should be run from the root of the project:
149149
150150
```shell
151151
python3 -m uvicorn fastapi_app:create_app --factory --reload

infra/main.bicep

Lines changed: 115 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ param principalId string = ''
3232
param openAILocation string
3333

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

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

50-
param azureOpenAiAPIVersion string = '2024-03-01-preview'
50+
param azureOpenAIAPIVersion string = '2024-03-01-preview'
51+
@secure()
52+
param azureOpenAIKey string = ''
53+
@description('Azure OpenAI endpoint to use, if not using the one deployed here.')
54+
param azureOpenAIEndpoint string = ''
55+
56+
@description('Whether to use Azure OpenAI (either deployed here or elsewhere) or OpenAI.com')
57+
var useAzureOpenAI = deployAzureOpenAI || !empty(azureOpenAIEndpoint)
5158

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

6976
var embedConfig = {
7077
modelName: !empty(embedModelName) ? embedModelName : 'text-embedding-ada-002'
71-
deploymentName: !empty(embedDeploymentName) ? embedDeploymentName : 'embed'
78+
deploymentName: !empty(embedDeploymentName) ? embedDeploymentName : 'text-embedding-ada-002'
7279
deploymentVersion: !empty(embedDeploymentVersion) ? embedDeploymentVersion : '2'
7380
deploymentCapacity: embedDeploymentCapacity != 0 ? embedDeploymentCapacity : 30
7481
dimensions: embedDimensions != 0 ? embedDimensions : 1536
@@ -145,6 +152,91 @@ module containerApps 'core/host/container-apps.bicep' = {
145152
// Web frontend
146153
var webAppName = replace('${take(prefix, 19)}-ca', '--', '-')
147154
var webAppIdentityName = '${prefix}-id-web'
155+
var webAppEnv = [
156+
{
157+
name: 'POSTGRES_HOST'
158+
value: postgresServer.outputs.POSTGRES_DOMAIN_NAME
159+
}
160+
{
161+
name: 'POSTGRES_USERNAME'
162+
value: webAppIdentityName
163+
}
164+
{
165+
name: 'POSTGRES_DATABASE'
166+
value: postgresDatabaseName
167+
}
168+
{
169+
name: 'POSTGRES_SSL'
170+
value: 'require'
171+
}
172+
{
173+
name: 'RUNNING_IN_PRODUCTION'
174+
value: 'true'
175+
}
176+
{
177+
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
178+
value: monitoring.outputs.applicationInsightsConnectionString
179+
}
180+
{
181+
name: 'OPENAI_CHAT_HOST'
182+
value: useAzureOpenAI ? 'azure' : 'openaicom'
183+
}
184+
{
185+
name: 'AZURE_OPENAI_CHAT_DEPLOYMENT'
186+
value: useAzureOpenAI ? chatConfig.deploymentName : ''
187+
}
188+
{
189+
name: 'AZURE_OPENAI_CHAT_MODEL'
190+
value: useAzureOpenAI ? chatConfig.modelName : ''
191+
}
192+
{
193+
name: 'OPENAICOM_CHAT_MODEL'
194+
value: useAzureOpenAI ? '' : 'gpt-3.5-turbo'
195+
}
196+
{
197+
name: 'OPENAI_EMBED_HOST'
198+
value: useAzureOpenAI ? 'azure' : 'openaicom'
199+
}
200+
{
201+
name: 'OPENAICOM_EMBED_MODEL_DIMENSIONS'
202+
value: useAzureOpenAI ? '' : '1536'
203+
}
204+
{
205+
name: 'OPENAICOM_EMBED_MODEL'
206+
value: useAzureOpenAI ? '' : 'text-embedding-ada-002'
207+
}
208+
{
209+
name: 'AZURE_OPENAI_EMBED_MODEL'
210+
value: useAzureOpenAI ? embedConfig.modelName : ''
211+
}
212+
{
213+
name: 'AZURE_OPENAI_EMBED_DEPLOYMENT'
214+
value: useAzureOpenAI ? embedConfig.deploymentName : ''
215+
}
216+
{
217+
name: 'AZURE_OPENAI_EMBED_MODEL_DIMENSIONS'
218+
value: useAzureOpenAI ? string(embedConfig.dimensions) : ''
219+
}
220+
{
221+
name: 'AZURE_OPENAI_ENDPOINT'
222+
value: useAzureOpenAI ? (deployAzureOpenAI ? openAI.outputs.endpoint : azureOpenAIEndpoint) : ''
223+
}
224+
{
225+
name: 'AZURE_OPENAI_VERSION'
226+
value: useAzureOpenAI ? azureOpenAIAPIVersion : ''
227+
}
228+
]
229+
var webAppEnvWithSecret = !empty(azureOpenAIKey) ? union(webAppEnv, [
230+
{
231+
name: 'AZURE_OPENAI_KEY'
232+
secretRef: 'azure-openai-key'
233+
}
234+
]) : webAppEnv
235+
236+
var secrets = !empty(azureOpenAIKey) ? {
237+
'azure-openai-key': azureOpenAIKey
238+
} : {}
239+
148240
module web 'web.bicep' = {
149241
name: 'web'
150242
scope: resourceGroup
@@ -156,91 +248,19 @@ module web 'web.bicep' = {
156248
containerAppsEnvironmentName: containerApps.outputs.environmentName
157249
containerRegistryName: containerApps.outputs.registryName
158250
exists: webAppExists
159-
environmentVariables: [
160-
{
161-
name: 'POSTGRES_HOST'
162-
value: postgresServer.outputs.POSTGRES_DOMAIN_NAME
163-
}
164-
{
165-
name: 'POSTGRES_USERNAME'
166-
value: webAppIdentityName
167-
}
168-
{
169-
name: 'POSTGRES_DATABASE'
170-
value: postgresDatabaseName
171-
}
172-
{
173-
name: 'POSTGRES_SSL'
174-
value: 'require'
175-
}
176-
{
177-
name: 'RUNNING_IN_PRODUCTION'
178-
value: 'true'
179-
}
180-
{
181-
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
182-
value: monitoring.outputs.applicationInsightsConnectionString
183-
}
184-
{
185-
name: 'OPENAI_CHAT_HOST'
186-
value: deployAzureOpenAI ? 'azure' : 'openaicom'
187-
}
188-
{
189-
name: 'AZURE_OPENAI_CHAT_DEPLOYMENT'
190-
value: deployAzureOpenAI ? chatConfig.deploymentName : ''
191-
}
192-
{
193-
name: 'AZURE_OPENAI_CHAT_MODEL'
194-
value: deployAzureOpenAI ? chatConfig.modelName : ''
195-
}
196-
{
197-
name: 'OPENAICOM_CHAT_MODEL'
198-
value: deployAzureOpenAI ? '' : 'gpt-3.5-turbo'
199-
}
200-
{
201-
name: 'OPENAI_EMBED_HOST'
202-
value: deployAzureOpenAI ? 'azure' : 'openaicom'
203-
}
204-
{
205-
name: 'OPENAICOM_EMBED_MODEL_DIMENSIONS'
206-
value: deployAzureOpenAI ? '' : '1536'
207-
}
208-
{
209-
name: 'OPENAICOM_EMBED_MODEL'
210-
value: deployAzureOpenAI ? '' : 'text-embedding-ada-002'
211-
}
212-
{
213-
name: 'AZURE_OPENAI_EMBED_MODEL'
214-
value: deployAzureOpenAI ? embedConfig.modelName : ''
215-
}
216-
{
217-
name: 'AZURE_OPENAI_EMBED_DEPLOYMENT'
218-
value: deployAzureOpenAI ? embedConfig.deploymentName : ''
219-
}
220-
{
221-
name: 'AZURE_OPENAI_EMBED_MODEL_DIMENSIONS'
222-
value: deployAzureOpenAI ? string(embedConfig.dimensions) : ''
223-
}
224-
{
225-
name: 'AZURE_OPENAI_ENDPOINT'
226-
value: deployAzureOpenAI ? openAi.outputs.endpoint : ''
227-
}
228-
{
229-
name: 'AZURE_OPENAI_VERSION'
230-
value: deployAzureOpenAI ? azureOpenAiAPIVersion : ''
231-
}
232-
]
251+
environmentVariables: webAppEnvWithSecret
252+
secrets: secrets
233253
}
234254
}
235255

236-
resource openAiResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' existing =
237-
if (!empty(openAiResourceGroupName)) {
238-
name: !empty(openAiResourceGroupName) ? openAiResourceGroupName : resourceGroup.name
256+
resource openAIResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' existing =
257+
if (!empty(openAIResourceGroupName)) {
258+
name: !empty(openAIResourceGroupName) ? openAIResourceGroupName : resourceGroup.name
239259
}
240260

241-
module openAi 'core/ai/cognitiveservices.bicep' = {
261+
module openAI 'core/ai/cognitiveservices.bicep' = if (deployAzureOpenAI) {
242262
name: 'openai'
243-
scope: openAiResourceGroup
263+
scope: openAIResourceGroup
244264
params: {
245265
name: '${prefix}-openai'
246266
location: openAILocation
@@ -279,9 +299,9 @@ module openAi 'core/ai/cognitiveservices.bicep' = {
279299
}
280300

281301
// USER ROLES
282-
module openAiRoleUser 'core/security/role.bicep' =
302+
module openAIRoleUser 'core/security/role.bicep' =
283303
if (empty(runningOnGh)) {
284-
scope: openAiResourceGroup
304+
scope: openAIResourceGroup
285305
name: 'openai-role-user'
286306
params: {
287307
principalId: principalId
@@ -291,8 +311,8 @@ module openAiRoleUser 'core/security/role.bicep' =
291311
}
292312

293313
// Backend roles
294-
module openAiRoleBackend 'core/security/role.bicep' = {
295-
scope: openAiResourceGroup
314+
module openAIRoleBackend 'core/security/role.bicep' = {
315+
scope: openAIResourceGroup
296316
name: 'openai-role-backend'
297317
params: {
298318
principalId: web.outputs.SERVICE_WEB_IDENTITY_PRINCIPAL_ID
@@ -314,13 +334,13 @@ output SERVICE_WEB_NAME string = web.outputs.SERVICE_WEB_NAME
314334
output SERVICE_WEB_URI string = web.outputs.SERVICE_WEB_URI
315335
output SERVICE_WEB_IMAGE_NAME string = web.outputs.SERVICE_WEB_IMAGE_NAME
316336

317-
output AZURE_OPENAI_ENDPOINT string = deployAzureOpenAI ? openAi.outputs.endpoint : ''
318-
output AZURE_OPENAI_VERSION string = deployAzureOpenAI ? azureOpenAiAPIVersion : ''
319-
output AZURE_OPENAI_CHAT_DEPLOYMENT string = deployAzureOpenAI ? chatConfig.deploymentName : ''
320-
output AZURE_OPENAI_EMBED_DEPLOYMENT string = deployAzureOpenAI ? embedConfig.deploymentName : ''
321-
output AZURE_OPENAI_CHAT_MODEL string = deployAzureOpenAI ? chatConfig.modelName : ''
322-
output AZURE_OPENAI_EMBED_MODEL string = deployAzureOpenAI ? embedConfig.modelName : ''
323-
output AZURE_OPENAI_EMBED_MODEL_DIMENSIONS int = deployAzureOpenAI ? embedConfig.dimensions : 0
337+
output AZURE_OPENAI_ENDPOINT string = useAzureOpenAI ? (deployAzureOpenAI ? openAI.outputs.endpoint : azureOpenAIEndpoint) : ''
338+
output AZURE_OPENAI_VERSION string = useAzureOpenAI ? azureOpenAIAPIVersion : ''
339+
output AZURE_OPENAI_CHAT_DEPLOYMENT string = useAzureOpenAI ? chatConfig.deploymentName : ''
340+
output AZURE_OPENAI_EMBED_DEPLOYMENT string = useAzureOpenAI ? embedConfig.deploymentName : ''
341+
output AZURE_OPENAI_CHAT_MODEL string = useAzureOpenAI ? chatConfig.modelName : ''
342+
output AZURE_OPENAI_EMBED_MODEL string = useAzureOpenAI ? embedConfig.modelName : ''
343+
output AZURE_OPENAI_EMBED_MODEL_DIMENSIONS int = useAzureOpenAI ? embedConfig.dimensions : 0
324344

325345
output POSTGRES_HOST string = postgresServer.outputs.POSTGRES_DOMAIN_NAME
326346
output POSTGRES_USERNAME string = postgresEntraAdministratorName

infra/main.parameters.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
"deployAzureOpenAI": {
2121
"value": "${DEPLOY_AZURE_OPENAI=true}"
2222
},
23+
"azureOpenAIKey": {
24+
"value": "${AZURE_OPENAI_KEY}"
25+
},
26+
"azureOpenAIEndpoint": {
27+
"value": "${AZURE_OPENAI_ENDPOINT}"
28+
},
2329
"chatModelName":{
2430
"value": "${AZURE_OPENAI_CHAT_MODEL}"
2531
},

infra/web.bicep

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ param exists bool
88
param identityName string
99
param serviceName string = 'web'
1010
param environmentVariables array = []
11+
@secure()
12+
param secrets object
1113

1214
resource webIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
1315
name: identityName
@@ -37,6 +39,7 @@ module app 'core/host/container-app-upsert.bicep' = {
3739
}
3840
]
3941
)
42+
secrets: secrets
4043
targetPort: 8000
4144
}
4245
}

src/fastapi_app/openai_clients.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,21 @@
1010
async def create_openai_chat_client(azure_credential):
1111
OPENAI_CHAT_HOST = os.getenv("OPENAI_CHAT_HOST")
1212
if OPENAI_CHAT_HOST == "azure":
13-
logger.info("Authenticating to OpenAI using Azure Identity...")
14-
15-
token_provider = azure.identity.get_bearer_token_provider(
16-
azure_credential, "https://cognitiveservices.azure.com/.default"
17-
)
13+
client_args = {}
14+
if api_key := os.getenv("AZURE_OPENAI_KEY"):
15+
logger.info("Authenticating to Azure OpenAI using API key...")
16+
client_args["api_key"] = api_key
17+
else:
18+
logger.info("Authenticating to Azure OpenAI using Azure Identity...")
19+
token_provider = azure.identity.get_bearer_token_provider(
20+
azure_credential, "https://cognitiveservices.azure.com/.default"
21+
)
22+
client_args["azure_ad_token_provider"] = token_provider
1823
openai_chat_client = openai.AsyncAzureOpenAI(
1924
api_version=os.getenv("AZURE_OPENAI_VERSION"),
2025
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
21-
azure_ad_token_provider=token_provider,
2226
azure_deployment=os.getenv("AZURE_OPENAI_CHAT_DEPLOYMENT"),
27+
**client_args,
2328
)
2429
openai_chat_model = os.getenv("AZURE_OPENAI_CHAT_MODEL")
2530
elif OPENAI_CHAT_HOST == "ollama":
@@ -40,14 +45,21 @@ async def create_openai_chat_client(azure_credential):
4045
async def create_openai_embed_client(azure_credential):
4146
OPENAI_EMBED_HOST = os.getenv("OPENAI_EMBED_HOST")
4247
if OPENAI_EMBED_HOST == "azure":
43-
token_provider = azure.identity.get_bearer_token_provider(
44-
azure_credential, "https://cognitiveservices.azure.com/.default"
45-
)
48+
client_args = {}
49+
if api_key := os.getenv("AZURE_OPENAI_KEY"):
50+
logger.info("Authenticating to Azure OpenAI using API key...")
51+
client_args["api_key"] = api_key
52+
else:
53+
logger.info("Authenticating to Azure OpenAI using Azure Identity...")
54+
token_provider = azure.identity.get_bearer_token_provider(
55+
azure_credential, "https://cognitiveservices.azure.com/.default"
56+
)
57+
client_args["azure_ad_token_provider"] = token_provider
4658
openai_embed_client = openai.AsyncAzureOpenAI(
4759
api_version=os.getenv("AZURE_OPENAI_VERSION"),
4860
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
49-
azure_ad_token_provider=token_provider,
5061
azure_deployment=os.getenv("AZURE_OPENAI_EMBED_DEPLOYMENT"),
62+
**client_args,
5163
)
5264
openai_embed_model = os.getenv("AZURE_OPENAI_EMBED_MODEL")
5365
openai_embed_dimensions = os.getenv("AZURE_OPENAI_EMBED_DIMENSIONS")

src/frontend/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<meta charset="UTF-8" />
55
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7-
<title>GPT + Enterprise data | Sample</title>
7+
<title>RAG on PostgreSQL</title>
88
</head>
99
<body>
1010
<div id="root"></div>

0 commit comments

Comments
 (0)