From eeb732d01c5c224c072155953a51817d61f26edd Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Tue, 23 Jul 2024 11:31:10 +0000 Subject: [PATCH 1/2] Properly support OpenAI.com keys --- README.md | 1 + azure.yaml | 2 +- docs/deploy_existing.md | 18 +++ infra/core/host/container-app-upsert.bicep | 5 - infra/core/host/container-app.bicep | 8 +- infra/main.bicep | 127 +++++++++++++-------- infra/main.parameters.json | 11 +- 7 files changed, 110 insertions(+), 62 deletions(-) create mode 100644 docs/deploy_existing.md diff --git a/README.md b/README.md index 882181c6..ce32320f 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ A related option is VS Code Dev Containers, which will open the project in your 1. Make sure the following tools are installed: * [Azure Developer CLI (azd)](https://aka.ms/install-azd) + * [Node.js 18+](https://nodejs.org/download/) * [Python 3.10+](https://www.python.org/downloads/) * [PostgreSQL 14+](https://www.postgresql.org/download/) * [pgvector](https://github.com/pgvector/pgvector) diff --git a/azure.yaml b/azure.yaml index 81069a73..aab8bf16 100644 --- a/azure.yaml +++ b/azure.yaml @@ -9,7 +9,7 @@ services: module: web host: containerapp hooks: - prepackage: + prebuild: windows: shell: pwsh run: cd ../frontend;npm install;npm run build diff --git a/docs/deploy_existing.md b/docs/deploy_existing.md new file mode 100644 index 00000000..e681d7a6 --- /dev/null +++ b/docs/deploy_existing.md @@ -0,0 +1,18 @@ +# Deploying with existing Azure resources + +If you already have existing Azure resources, or if you want to specify the exact name of new Azure Resource, you can do so by setting `azd` environment values. +You should set these values before running `azd up`. Once you've set them, return to the [deployment steps](../README.md#deployment). + +### Openai.com OpenAI account + +1. Run `azd env set DEPLOY_AZURE_OPENAI false` +1. Run `azd env set OPENAI_CHAT_HOST openaicom` +2. Run `azd env set OPENAI_EMBED_HOST openaicom` +3. Run `azd env set OPENAICOM_KEY {Your OpenAI API key}` +4. Run `azd up` + +You can retrieve your OpenAI key by checking [your user page](https://platform.openai.com/account/api-keys). +Learn more about creating an OpenAI free trial at [this link](https://openai.com/pricing). +Do *not* check your key into source control. + +When you run `azd up` after and are prompted to select a value for `openAiResourceGroupLocation`, you can select any location as it will not be used. diff --git a/infra/core/host/container-app-upsert.bicep b/infra/core/host/container-app-upsert.bicep index fe9cd9b2..3577d412 100644 --- a/infra/core/host/container-app-upsert.bicep +++ b/infra/core/host/container-app-upsert.bicep @@ -75,10 +75,6 @@ param serviceBinds array = [] @description('The target port for the container') param targetPort int = 80 -// Service options -@description('PostgreSQL service ID') -param postgresServiceId string = '' - resource existingApp 'Microsoft.App/containerApps@2023-05-02-preview' existing = if (exists) { name: name } @@ -103,7 +99,6 @@ module app 'container-app.bicep' = { daprEnabled: daprEnabled daprAppId: daprAppId daprAppProtocol: daprAppProtocol - postgresServiceId: postgresServiceId secrets: secrets keyvaultIdentities: keyvaultIdentities external: external diff --git a/infra/core/host/container-app.bicep b/infra/core/host/container-app.bicep index 0babf0aa..225d0681 100644 --- a/infra/core/host/container-app.bicep +++ b/infra/core/host/container-app.bicep @@ -31,10 +31,6 @@ param containerRegistryName string = '' @description('Hostname suffix for container registry. Set when deploying to sovereign clouds') param containerRegistryHostSuffix string = 'azurecr.io' -// Service options -@description('PostgreSQL service ID') -param postgresServiceId string = '' - @description('The protocol used by Dapr to connect to the app, e.g., http or grpc') @allowed([ 'http', 'grpc' ]) param daprAppProtocol string = 'http' @@ -102,7 +98,7 @@ var keyvaultIdentitySecrets = [for secret in items(keyvaultIdentities): { name: secret.key keyVaultUrl: secret.value.keyVaultUrl identity: secret.value.identity -}] +}] module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { name: '${deployment().name}-registry-access' @@ -118,7 +114,7 @@ resource app 'Microsoft.App/containerApps@2023-05-02-preview' = { tags: tags // It is critical that the identity is granted ACR pull access before the app is created // otherwise the container app will throw a provision error - // This also forces us to use an user assigned managed identity since there would no way to + // This also forces us to use an user assigned managed identity since there would no way to // provide the system assigned identity with the ACR pull access before the app is created dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] identity: { diff --git a/infra/main.bicep b/infra/main.bicep index 58f4db04..2f823752 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -37,6 +37,21 @@ param openAIResourceGroupName string = '' @description('Whether to deploy Azure OpenAI resources') param deployAzureOpenAI bool = true +@allowed([ + 'azure' + 'openaicom' +]) +param openAIChatHost string = 'azure' + +@allowed([ + 'azure' + 'openaicom' +]) +param openAIEmbedHost string = 'azure' + +@secure() +param openAIComKey string = '' + @description('Name of the GPT model to deploy') param chatModelName string = '' @description('Name of the model deployment') @@ -50,18 +65,16 @@ param chatDeploymentVersion string = '' 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 : (useAzureOpenAI ? 'gpt-35-turbo' : 'gpt-3.5-turbo') + modelName: !empty(chatModelName) ? chatModelName : (openAIChatHost == 'azure' ? 'gpt-35-turbo' : 'gpt-3.5-turbo') deploymentName: !empty(chatDeploymentName) ? chatDeploymentName : 'gpt-35-turbo' deploymentVersion: !empty(chatDeploymentVersion) ? chatDeploymentVersion : '0125' deploymentCapacity: chatDeploymentCapacity != 0 ? chatDeploymentCapacity : 30 @@ -152,7 +165,37 @@ module containerApps 'core/host/container-apps.bicep' = { // Web frontend var webAppName = replace('${take(prefix, 19)}-ca', '--', '-') var webAppIdentityName = '${prefix}-id-web' -var webAppEnv = [ + +var azureOpenAiKeySecret = !empty(azureOpenAIKey) + ? { + 'azure-openai-key': azureOpenAIKey + } + : {} +var openAiComKeySecret = !empty(openAIComKey) + ? { + 'openaicom-key': openAIComKey + } + : {} +var secrets = union(azureOpenAiKeySecret, openAiComKeySecret) + +var azureOpenAIKeyEnv = !empty(azureOpenAIKey) + ? [ + { + name: 'AZURE_OPENAI_KEY' + secretRef: 'azure-openai-key' + } + ] + : [] +var openAIComKeyEnv = !empty(openAIComKey) + ? [ + { + name: 'OPENAICOM_KEY' + secretRef: 'openaicom-key' + } + ] + : [] + +var webAppEnv = union(azureOpenAIKeyEnv, openAIComKeyEnv, [ { name: 'POSTGRES_HOST' value: postgresServer.outputs.POSTGRES_DOMAIN_NAME @@ -179,63 +222,53 @@ var webAppEnv = [ } { name: 'OPENAI_CHAT_HOST' - value: useAzureOpenAI ? 'azure' : 'openaicom' + value: openAIChatHost } { name: 'AZURE_OPENAI_CHAT_DEPLOYMENT' - value: useAzureOpenAI ? chatConfig.deploymentName : '' + value: openAIChatHost == 'azure' ? chatConfig.deploymentName : '' } { name: 'AZURE_OPENAI_CHAT_MODEL' - value: useAzureOpenAI ? chatConfig.modelName : '' + value: openAIChatHost == 'azure' ? chatConfig.modelName : '' } { name: 'OPENAICOM_CHAT_MODEL' - value: useAzureOpenAI ? '' : 'gpt-3.5-turbo' + value: openAIChatHost == 'openaicom' ? 'gpt-3.5-turbo' : '' } { name: 'OPENAI_EMBED_HOST' - value: useAzureOpenAI ? 'azure' : 'openaicom' + value: openAIEmbedHost } { name: 'OPENAICOM_EMBED_MODEL_DIMENSIONS' - value: useAzureOpenAI ? '' : '1536' + value: openAIEmbedHost == 'openaicom' ? '1536' : '' } { name: 'OPENAICOM_EMBED_MODEL' - value: useAzureOpenAI ? '' : 'text-embedding-ada-002' + value: openAIEmbedHost == 'openaicom' ? 'text-embedding-ada-002' : '' } { name: 'AZURE_OPENAI_EMBED_MODEL' - value: useAzureOpenAI ? embedConfig.modelName : '' + value: openAIEmbedHost == 'azure' ? embedConfig.modelName : '' } { name: 'AZURE_OPENAI_EMBED_DEPLOYMENT' - value: useAzureOpenAI ? embedConfig.deploymentName : '' + value: openAIEmbedHost == 'azure' ? embedConfig.deploymentName : '' } { name: 'AZURE_OPENAI_EMBED_MODEL_DIMENSIONS' - value: useAzureOpenAI ? string(embedConfig.dimensions) : '' + value: openAIEmbedHost == 'azure' ? string(embedConfig.dimensions) : '' } { name: 'AZURE_OPENAI_ENDPOINT' - value: useAzureOpenAI ? (deployAzureOpenAI ? openAI.outputs.endpoint : azureOpenAIEndpoint) : '' + value: !empty(azureOpenAIEndpoint) ? azureOpenAIEndpoint : (deployAzureOpenAI ? openAI.outputs.endpoint : '') } { name: 'AZURE_OPENAI_VERSION' - value: useAzureOpenAI ? azureOpenAIAPIVersion : '' - } -] -var webAppEnvWithSecret = !empty(azureOpenAIKey) ? union(webAppEnv, [ - { - name: 'AZURE_OPENAI_KEY' - secretRef: 'azure-openai-key' + value: openAIEmbedHost == 'azure' ? azureOpenAIAPIVersion : '' } -]) : webAppEnv - -var secrets = !empty(azureOpenAIKey) ? { - 'azure-openai-key': azureOpenAIKey -} : {} +]) module web 'web.bicep' = { name: 'web' @@ -248,15 +281,14 @@ module web 'web.bicep' = { containerAppsEnvironmentName: containerApps.outputs.environmentName containerRegistryName: containerApps.outputs.registryName exists: webAppExists - environmentVariables: webAppEnvWithSecret + environmentVariables: webAppEnv 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' = if (deployAzureOpenAI) { name: 'openai' @@ -299,16 +331,15 @@ module openAI 'core/ai/cognitiveservices.bicep' = if (deployAzureOpenAI) { } // USER ROLES -module openAIRoleUser 'core/security/role.bicep' = - if (empty(runningOnGh)) { - scope: openAIResourceGroup - name: 'openai-role-user' - params: { - principalId: principalId - roleDefinitionId: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' - principalType: 'User' - } +module openAIRoleUser 'core/security/role.bicep' = if (empty(runningOnGh)) { + scope: openAIResourceGroup + name: 'openai-role-user' + params: { + principalId: principalId + roleDefinitionId: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' + principalType: 'User' } +} // Backend roles module openAIRoleBackend 'core/security/role.bicep' = { @@ -334,13 +365,11 @@ 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 = 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 AZURE_OPENAI_ENDPOINT string = !empty(azureOpenAIEndpoint) + ? azureOpenAIEndpoint + : (deployAzureOpenAI ? openAI.outputs.endpoint : '') +output AZURE_OPENAI_CHAT_DEPLOYMENT string = deployAzureOpenAI ? chatConfig.deploymentName : '' +output AZURE_OPENAI_EMBED_DEPLOYMENT string = deployAzureOpenAI ? embedConfig.deploymentName : '' output POSTGRES_HOST string = postgresServer.outputs.POSTGRES_DOMAIN_NAME output POSTGRES_USERNAME string = postgresEntraAdministratorName diff --git a/infra/main.parameters.json b/infra/main.parameters.json index 04122069..5db5f3cb 100644 --- a/infra/main.parameters.json +++ b/infra/main.parameters.json @@ -26,7 +26,10 @@ "azureOpenAIEndpoint": { "value": "${AZURE_OPENAI_ENDPOINT}" }, - "chatModelName":{ + "openAiChatHost": { + "value": "${OPENAI_CHAT_HOST=azure}" + }, + "chatModelName": { "value": "${AZURE_OPENAI_CHAT_MODEL}" }, "chatDeploymentName": { @@ -38,6 +41,9 @@ "chatDeploymentCapacity":{ "value": "${AZURE_OPENAI_CHAT_DEPLOYMENT_CAPACITY}" }, + "openAiEmbedHost": { + "value": "${OPENAI_EMBED_HOST=azure}" + }, "embedModelName":{ "value": "${AZURE_OPENAI_EMBED_MODEL}" }, @@ -52,6 +58,9 @@ }, "embedDimensions": { "value": "${AZURE_OPENAI_EMBED_DIMENSIONS}" + }, + "openAIComKey": { + "value": "${OPENAICOM_KEY}" } } } From 2d295bb3eefac8e8758495a217629da7dd12b72e Mon Sep 17 00:00:00 2001 From: Pamela Fox Date: Tue, 23 Jul 2024 11:36:28 +0000 Subject: [PATCH 2/2] Fix casing --- README.md | 2 ++ infra/main.bicep | 6 +++--- infra/main.parameters.json | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ce32320f..c5146d96 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,8 @@ Once you've opened the project in [Codespaces](#github-codespaces), [Dev Contain This will create a folder under `.azure/` in your project to store the configuration for this deployment. You may have multiple azd environments if desired. +3. (Optional) If you would like to customize the deployment to [use existing Azure resources](docs/deploy_existing.md), you can set the values now. + 3. Provision the resources and deploy the code: ```shell diff --git a/infra/main.bicep b/infra/main.bicep index 2f823752..f0e3f32b 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -166,17 +166,17 @@ module containerApps 'core/host/container-apps.bicep' = { var webAppName = replace('${take(prefix, 19)}-ca', '--', '-') var webAppIdentityName = '${prefix}-id-web' -var azureOpenAiKeySecret = !empty(azureOpenAIKey) +var azureOpenAIKeySecret = !empty(azureOpenAIKey) ? { 'azure-openai-key': azureOpenAIKey } : {} -var openAiComKeySecret = !empty(openAIComKey) +var openAIComKeySecret = !empty(openAIComKey) ? { 'openaicom-key': openAIComKey } : {} -var secrets = union(azureOpenAiKeySecret, openAiComKeySecret) +var secrets = union(azureOpenAIKeySecret, openAIComKeySecret) var azureOpenAIKeyEnv = !empty(azureOpenAIKey) ? [ diff --git a/infra/main.parameters.json b/infra/main.parameters.json index 5db5f3cb..c31cc033 100644 --- a/infra/main.parameters.json +++ b/infra/main.parameters.json @@ -26,7 +26,7 @@ "azureOpenAIEndpoint": { "value": "${AZURE_OPENAI_ENDPOINT}" }, - "openAiChatHost": { + "openAIChatHost": { "value": "${OPENAI_CHAT_HOST=azure}" }, "chatModelName": { @@ -41,7 +41,7 @@ "chatDeploymentCapacity":{ "value": "${AZURE_OPENAI_CHAT_DEPLOYMENT_CAPACITY}" }, - "openAiEmbedHost": { + "openAIEmbedHost": { "value": "${OPENAI_EMBED_HOST=azure}" }, "embedModelName":{