From f61234f2470b4ae402afcaa08bfb4ea8e5c7a7c1 Mon Sep 17 00:00:00 2001 From: "Vishal Shinde (Persistent Systems Inc)" Date: Tue, 10 Jun 2025 17:13:45 +0530 Subject: [PATCH 01/16] US:18890-Standardize Bicep Parameters for MACAE --- docs/CustomizingAzdParameters.md | 29 +++++++++++++++++++++++++++++ documentation/DeploymentGuide.md | 18 +++++++++--------- infra/main.bicepparam | 3 ++- infra/main.waf-aligned.bicepparam | 3 ++- 4 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 docs/CustomizingAzdParameters.md diff --git a/docs/CustomizingAzdParameters.md b/docs/CustomizingAzdParameters.md new file mode 100644 index 000000000..0d4bbb0be --- /dev/null +++ b/docs/CustomizingAzdParameters.md @@ -0,0 +1,29 @@ +## [Optional]: Customizing resource names + +By default this template will use the environment name as the prefix to prevent naming collisions within Azure. The parameters below show the default values. You only need to run the statements below if you need to change the values. + +> To override any of the parameters, run `azd env set ` before running `azd up`. On the first azd command, it will prompt you for the environment name. Be sure to choose 3-20 characters alphanumeric unique name. + +## Parameters +| Name | Type | Default Value | Purpose | +| ---------------------------- | ------ | ----------------- | --------------------------------------------------------------------------------------------------- | +| `AZURE_ENV_NAME` | string | `macae` | Used as a prefix for all resource names to ensure uniqueness across environments. | +| `AZURE_LOCATION` | string | `japaneast` | Location of the Azure resources. Controls where the infrastructure will be deployed. | +| `AZURE_ENV_OPENAI_LOCATION` | string | `swedencentral` | Specifies the region for OpenAI resource deployment. | +| `AZURE_ENV_ENABLE_TELEMETRY` | bool | `true` | Enables telemetry for monitoring and diagnostics. | + +--- + +## How to Set a Parameter + +To customize any of the above values, run the following command **before** `azd up`: + +```bash +azd env set +``` + +**Example:** + +```bash +azd env set AZURE_LOCATION westus2 +``` diff --git a/documentation/DeploymentGuide.md b/documentation/DeploymentGuide.md index 420bfe90d..5d100ed00 100644 --- a/documentation/DeploymentGuide.md +++ b/documentation/DeploymentGuide.md @@ -134,15 +134,15 @@ Consider the following settings during your deployment to modify specific settin
Configurable Deployment Settings -When you start the deployment, most parameters will have **default values**, but you can update the following settings: - -| **Setting** | **Description** | **Default value** | -| --------------------------------- | ------------------------------------------------------------------------------------------- | ----------------- | -| **Azure Region** | The region where resources will be created. | East US | -| **Secondary Location** | A **less busy** region for **Azure Cosmos DB**, useful in case of availability constraints. | eastus2 | -| **Deployment Type** | Select from a drop-down list. | GlobalStandard | -| **GPT Model** | Choose from **gpt-4, gpt-4o, gpt-4o-mini**. | gpt-4o | -| **GPT Model Deployment Capacity** | Configure capacity for **GPT models**. | 140k | +When you start the deployment, most parameters will have **default values**, but you can update the following settings [here](../docs/CustomizingAzdParameters.md): + +| **Setting** | **Description** | **Default value** | +| ------------------------------ | ------------------------------------------------------------------------------------ | ----------------- | +| **Environment Name** | Used as a prefix for all resource names to ensure uniqueness across environments. | macae | +| **Azure Region** | Location of the Azure resources. Controls where the infrastructure will be deployed. | japaneast | +| **OpenAI Deployment Location** | Specifies the region for OpenAI resource deployment. | swedencentral | +| **Enable Telemetry** | Enables telemetry for monitoring and diagnostics. | true | +
diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 69723341e..7a2203e85 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -1,8 +1,9 @@ using './main.bicep' -param solutionPrefix = null //Type a string value to customize the prefix for your resource names +param solutionPrefix = readEnvironmentVariable('AZURE_ENV_NAME', 'macae') param solutionLocation = readEnvironmentVariable('AZURE_LOCATION', 'swedencentral') param azureOpenAILocation = readEnvironmentVariable('AZURE_ENV_OPENAI_LOCATION', 'swedencentral') +param enableTelemetry = bool(readEnvironmentVariable('AZURE_ENV_ENABLE_TELEMETRY', 'true')) param logAnalyticsWorkspaceConfiguration = { dataRetentionInDays: 30 existingWorkspaceResourceId: '' diff --git a/infra/main.waf-aligned.bicepparam b/infra/main.waf-aligned.bicepparam index 0a226da80..d913c227c 100644 --- a/infra/main.waf-aligned.bicepparam +++ b/infra/main.waf-aligned.bicepparam @@ -1,8 +1,9 @@ using './main.bicep' -param solutionPrefix = null //Type a string value to customize the prefix for your resource names +param solutionPrefix = readEnvironmentVariable('AZURE_ENV_NAME', 'macae') param solutionLocation = readEnvironmentVariable('AZURE_LOCATION', 'swedencentral') param azureOpenAILocation = readEnvironmentVariable('AZURE_ENV_OPENAI_LOCATION', 'swedencentral') +param enableTelemetry = bool(readEnvironmentVariable('AZURE_ENV_ENABLE_TELEMETRY', 'true')) param virtualMachineConfiguration = { adminUsername: 'adminuser' adminPassword: 'P@ssw0rd1234' From 4782a3e044fcd37d0ee36594370863a892d5fcb2 Mon Sep 17 00:00:00 2001 From: "Vishal Shinde (Persistent Systems Inc)" Date: Thu, 12 Jun 2025 14:01:15 +0530 Subject: [PATCH 02/16] added more paramter in bicep file and md file --- docs/CustomizingAzdParameters.md | 17 +++++++++++------ docs/DeploymentGuide.md | 6 +++++- infra/main.bicep | 23 ++++++++++++++++++----- infra/main.bicepparam | 4 ++++ infra/main.waf-aligned.bicepparam | 4 ++++ 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/docs/CustomizingAzdParameters.md b/docs/CustomizingAzdParameters.md index 0d4bbb0be..b4e194726 100644 --- a/docs/CustomizingAzdParameters.md +++ b/docs/CustomizingAzdParameters.md @@ -5,12 +5,17 @@ By default this template will use the environment name as the prefix to prevent > To override any of the parameters, run `azd env set ` before running `azd up`. On the first azd command, it will prompt you for the environment name. Be sure to choose 3-20 characters alphanumeric unique name. ## Parameters -| Name | Type | Default Value | Purpose | -| ---------------------------- | ------ | ----------------- | --------------------------------------------------------------------------------------------------- | -| `AZURE_ENV_NAME` | string | `macae` | Used as a prefix for all resource names to ensure uniqueness across environments. | -| `AZURE_LOCATION` | string | `japaneast` | Location of the Azure resources. Controls where the infrastructure will be deployed. | -| `AZURE_ENV_OPENAI_LOCATION` | string | `swedencentral` | Specifies the region for OpenAI resource deployment. | -| `AZURE_ENV_ENABLE_TELEMETRY` | bool | `true` | Enables telemetry for monitoring and diagnostics. | + +| Name | Type | Default Value | Purpose | +| ------------------------------- | ------ | ----------------- | --------------------------------------------------------------------------------------------------- | +| `AZURE_ENV_NAME` | string | `macae` | Used as a prefix for all resource names to ensure uniqueness across environments. | +| `AZURE_LOCATION` | string | `swedencentral` | Location of the Azure resources. Controls where the infrastructure will be deployed. | +| `AZURE_ENV_OPENAI_LOCATION` | string | `swedencentral` | Specifies the region for OpenAI resource deployment. | +| `AZURE_ENV_MODEL_DEPLOYMENT_TYPE` | string | `GlobalStandard` | Defines the deployment type for the AI model (e.g., Standard, GlobalStandard). | +| `AZURE_ENV_MODEL_NAME` | string | `gpt-4o` | Specifies the name of the GPT model to be deployed. | +| `AZURE_ENV_MODEL_VERSION` | string | `2024-08-06` | Version of the GPT model to be used for deployment. | +| `AZURE_ENV_IMAGETAG` | string | `latest` | Docker image tag used for container deployments. | +| `AZURE_ENV_ENABLE_TELEMETRY` | bool | `true` | Enables telemetry for monitoring and diagnostics. | --- diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index d89f8e85b..c45603940 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -139,8 +139,12 @@ When you start the deployment, most parameters will have **default values**, but | **Setting** | **Description** | **Default value** | | ------------------------------ | ------------------------------------------------------------------------------------ | ----------------- | | **Environment Name** | Used as a prefix for all resource names to ensure uniqueness across environments. | macae | -| **Azure Region** | Location of the Azure resources. Controls where the infrastructure will be deployed. | japaneast | +| **Azure Region** | Location of the Azure resources. Controls where the infrastructure will be deployed. | swedencentral | | **OpenAI Deployment Location** | Specifies the region for OpenAI resource deployment. | swedencentral | +| **Model Deployment Type** | Defines the deployment type for the AI model (e.g., Standard, GlobalStandard). | GlobalStandard | +| **GPT Model Name** | Specifies the name of the GPT model to be deployed. | gpt-4o | +| **GPT Model Version** | Version of the GPT model to be used for deployment. | 2024-08-06 | +| **Image Tag** | Docker image tag used for container deployments. | latest | | **Enable Telemetry** | Enables telemetry for monitoring and diagnostics. | true | diff --git a/infra/main.bicep b/infra/main.bicep index 9991ad766..b0552b5cc 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -16,6 +16,19 @@ param enableTelemetry bool = true @description('Azure OpenAI Location') param azureOpenAILocation string +@minLength(1) +@description('Name of the GPT model to deploy:') +param gptModelName string = 'gpt-4o' + +param gptModelVersion string = '2024-08-06' + +@minLength(1) +@description('GPT model deployment type:') +param modelDeploymentType string = 'GlobalStandard' + +@description('Set the image tag for the container images used in the solution. Default is "latest".') +param imageTag string = 'latest' + // @description('Set this if you want to deploy to a different region than the resource group. Otherwise, it will use the resource group location by default.') // param AZURE_LOCATION string='' // param solutionLocation string = empty(AZURE_LOCATION) ? resourceGroup().location @@ -194,7 +207,7 @@ param containerAppConfiguration containerAppConfigurationType = { containerMemory: '4.0Gi' containerImageRegistryDomain: 'biabcontainerreg.azurecr.io' containerImageName: 'macaebackend' - containerImageTag: 'latest' + containerImageTag: imageTag containerName: 'backend' ingressTargetPort: 8000 maxReplicas: 1 @@ -218,7 +231,7 @@ param webSiteConfiguration webSiteConfigurationType = { location: solutionLocation containerImageRegistryDomain: 'biabcontainerreg.azurecr.io' containerImageName: 'macaefrontend' - containerImageTag: 'latest' + containerImageTag: imageTag containerName: 'backend' tags: tags environmentResourceId: null //Default value set on module configuration @@ -731,10 +744,10 @@ var aiFoundryAiServicesResourceName = aiFoundryAiServicesConfiguration.?name ?? var aiFoundryAIservicesEnabled = aiFoundryAiServicesConfiguration.?enabled ?? true var aiFoundryAiServicesModelDeployment = { format: 'OpenAI' - name: 'gpt-4o' - version: '2024-08-06' + name: gptModelName + version: gptModelVersion sku: { - name: 'GlobalStandard' + name: modelDeploymentType //Curently the capacity is set to 140 for opinanal performance. capacity: aiFoundryAiServicesConfiguration.?modelCapacity ?? 140 } diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 7a2203e85..5f32de008 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -3,6 +3,10 @@ using './main.bicep' param solutionPrefix = readEnvironmentVariable('AZURE_ENV_NAME', 'macae') param solutionLocation = readEnvironmentVariable('AZURE_LOCATION', 'swedencentral') param azureOpenAILocation = readEnvironmentVariable('AZURE_ENV_OPENAI_LOCATION', 'swedencentral') +param modelDeploymentType = readEnvironmentVariable('AZURE_ENV_MODEL_DEPLOYMENT_TYPE', 'GlobalStandard') +param gptModelName = readEnvironmentVariable('AZURE_ENV_MODEL_NAME', 'gpt-4o') +param gptModelVersion = readEnvironmentVariable('AZURE_ENV_MODEL_VERSION', '2024-08-06') +param imageTag = readEnvironmentVariable('AZURE_ENV_IMAGETAG', 'latest') param enableTelemetry = bool(readEnvironmentVariable('AZURE_ENV_ENABLE_TELEMETRY', 'true')) param logAnalyticsWorkspaceConfiguration = { dataRetentionInDays: 30 diff --git a/infra/main.waf-aligned.bicepparam b/infra/main.waf-aligned.bicepparam index d913c227c..ac45cdcf3 100644 --- a/infra/main.waf-aligned.bicepparam +++ b/infra/main.waf-aligned.bicepparam @@ -3,6 +3,10 @@ using './main.bicep' param solutionPrefix = readEnvironmentVariable('AZURE_ENV_NAME', 'macae') param solutionLocation = readEnvironmentVariable('AZURE_LOCATION', 'swedencentral') param azureOpenAILocation = readEnvironmentVariable('AZURE_ENV_OPENAI_LOCATION', 'swedencentral') +param modelDeploymentType = readEnvironmentVariable('AZURE_ENV_MODEL_DEPLOYMENT_TYPE', 'GlobalStandard') +param gptModelName = readEnvironmentVariable('AZURE_ENV_MODEL_NAME', 'gpt-4o') +param gptModelVersion = readEnvironmentVariable('AZURE_ENV_MODEL_VERSION', '2024-08-06') +param imageTag = readEnvironmentVariable('AZURE_ENV_IMAGETAG', 'latest') param enableTelemetry = bool(readEnvironmentVariable('AZURE_ENV_ENABLE_TELEMETRY', 'true')) param virtualMachineConfiguration = { adminUsername: 'adminuser' From a074b9c1db86a6fad13522f0950e3a9c2de85619 Mon Sep 17 00:00:00 2001 From: Abdul-Microsoft Date: Fri, 13 Jun 2025 16:50:45 +0530 Subject: [PATCH 03/16] FDP changes --- infra/main.bicep | 447 ++++++++++++++++++++++++------------------ infra/main.bicepparam | 6 +- 2 files changed, 258 insertions(+), 195 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index b0552b5cc..37c56a591 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -147,30 +147,30 @@ param aiFoundryAiServicesConfiguration aiServicesConfigurationType = { modelCapacity: 140 } -@description('Optional. The configuration to apply for the AI Foundry Storage Account resource.') -param aiFoundryStorageAccountConfiguration storageAccountType = { - enabled: true - name: replace('sthub${solutionPrefix}', '-', '') - location: azureOpenAILocation - tags: tags - sku: 'Standard_ZRS' - subnetResourceId: null //Default value set on module configuration -} - -@description('Optional. The configuration to apply for the AI Foundry AI Hub resource.') -param aiFoundryAiHubConfiguration aiHubType = { - enabled: true - name: 'aih-${solutionPrefix}' - location: azureOpenAILocation - sku: 'Basic' - tags: tags - subnetResourceId: null //Default value set on module configuration -} +// @description('Optional. The configuration to apply for the AI Foundry Storage Account resource.') +// param aiFoundryStorageAccountConfiguration storageAccountType = { +// enabled: true +// name: replace('sthub${solutionPrefix}', '-', '') +// location: azureOpenAILocation +// tags: tags +// sku: 'Standard_ZRS' +// subnetResourceId: null //Default value set on module configuration +// } + +// @description('Optional. The configuration to apply for the AI Foundry AI Hub resource.') +// param aiFoundryAiHubConfiguration aiHubType = { +// enabled: true +// name: 'aih-${solutionPrefix}' +// location: azureOpenAILocation +// sku: 'Basic' +// tags: tags +// subnetResourceId: null //Default value set on module configuration +// } @description('Optional. The configuration to apply for the AI Foundry AI Project resource.') param aiFoundryAiProjectConfiguration aiProjectConfigurationType = { enabled: true - name: 'aihb-${solutionPrefix}' + name: 'aifp-${solutionPrefix}' location: azureOpenAILocation sku: 'Basic' tags: tags @@ -754,7 +754,7 @@ var aiFoundryAiServicesModelDeployment = { raiPolicyName: 'Microsoft.Default' } -module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.10.2' = if (aiFoundryAIservicesEnabled) { +module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.11.0' = if (aiFoundryAIservicesEnabled) { name: take('avm.res.cognitive-services.account.${aiFoundryAiServicesResourceName}', 64) params: { name: aiFoundryAiServicesResourceName @@ -769,6 +769,10 @@ module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.10.2' apiProperties: { //staticsEnabled: false } + allowProjectManagement: true + managedIdentities: { + systemAssigned: true + } //publicNetworkAccess: virtualNetworkEnabled ? 'Disabled' : 'Enabled' //publicNetworkAccess: virtualNetworkEnabled ? 'Disabled' : 'Enabled' publicNetworkAccess: 'Enabled' //TODO: connection via private endpoint is not working from containers network. Change this when fixed @@ -798,6 +802,11 @@ module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.10.2' principalType: 'ServicePrincipal' roleDefinitionIdOrName: 'Cognitive Services OpenAI User' } + { + principalId: containerApp.outputs.?systemAssignedMIPrincipalId! + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '53ca6127-db72-4b80-b1b0-d745d6d5456d' + } ] deployments: aiFoundryAiServicesConfiguration.?deployments ?? [ { @@ -819,168 +828,214 @@ module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.10.2' // AI Foundry: storage account // WAF best practices for Azure Blob Storage: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-blob-storage -var storageAccountPrivateDnsZones = { - 'privatelink.blob.${environment().suffixes.storage}': 'blob' - 'privatelink.file.${environment().suffixes.storage}': 'file' +// var storageAccountPrivateDnsZones = { +// 'privatelink.blob.${environment().suffixes.storage}': 'blob' +// 'privatelink.file.${environment().suffixes.storage}': 'file' +// } + +// module privateDnsZonesAiFoundryStorageAccount 'br/public:avm/res/network/private-dns-zone:0.3.1' = [ +// for zone in objectKeys(storageAccountPrivateDnsZones): if (virtualNetworkEnabled && aiFoundryStorageAccountEnabled) { +// name: take( +// 'avm.res.network.private-dns-zone.storage-account.${uniqueString(aiFoundryStorageAccountResourceName,zone)}.${solutionPrefix}', +// 64 +// ) +// params: { +// name: zone +// tags: tags +// enableTelemetry: enableTelemetry +// virtualNetworkLinks: [ +// { +// name: 'vnetlink-${split(zone, '.')[1]}' +// virtualNetworkResourceId: virtualNetwork.outputs.resourceId +// } +// ] +// } +// } +// ] +// var aiFoundryStorageAccountEnabled = aiFoundryStorageAccountConfiguration.?enabled ?? true +// var aiFoundryStorageAccountResourceName = aiFoundryStorageAccountConfiguration.?name ?? replace( +// 'sthub${solutionPrefix}', +// '-', +// '' +// ) + +// module aiFoundryStorageAccount 'br/public:avm/res/storage/storage-account:0.18.2' = if (aiFoundryStorageAccountEnabled) { +// name: take('avm.res.storage.storage-account.${aiFoundryStorageAccountResourceName}', 64) +// dependsOn: [ +// privateDnsZonesAiFoundryStorageAccount +// ] +// params: { +// name: aiFoundryStorageAccountResourceName +// location: aiFoundryStorageAccountConfiguration.?location ?? azureOpenAILocation +// tags: aiFoundryStorageAccountConfiguration.?tags ?? tags +// enableTelemetry: enableTelemetry +// diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] +// skuName: aiFoundryStorageAccountConfiguration.?sku ?? 'Standard_ZRS' +// allowSharedKeyAccess: false +// networkAcls: { +// bypass: 'AzureServices' +// defaultAction: 'Allow' +// } +// blobServices: { +// deleteRetentionPolicyEnabled: false +// containerDeleteRetentionPolicyDays: 7 +// containerDeleteRetentionPolicyEnabled: false +// diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] +// } +// publicNetworkAccess: virtualNetworkEnabled ? 'Disabled' : 'Enabled' +// allowBlobPublicAccess: false +// privateEndpoints: virtualNetworkEnabled +// ? map(items(storageAccountPrivateDnsZones), zone => { +// name: 'pep-${zone.value}-${aiFoundryStorageAccountResourceName}' +// customNetworkInterfaceName: 'nic-${zone.value}-${aiFoundryStorageAccountResourceName}' +// service: zone.value +// subnetResourceId: aiFoundryStorageAccountConfiguration.?subnetResourceId ?? virtualNetwork.outputs.subnetResourceIds[0] ?? '' +// privateDnsZoneResourceIds: [resourceId('Microsoft.Network/privateDnsZones', zone.key)] +// }) +// : null +// roleAssignments: [ +// { +// principalId: userAssignedIdentity.outputs.principalId +// roleDefinitionIdOrName: 'Storage Blob Data Contributor' +// } +// ] +// } +// } + +// AI Foundry: AI Hub +// WAF best practices for Open AI: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-openai +// var mlTargetSubResource = 'amlworkspace' +// var mlPrivateDnsZones = { +// 'privatelink.api.azureml.ms': mlTargetSubResource +// 'privatelink.notebooks.azure.net': mlTargetSubResource +// } +// module privateDnsZonesAiFoundryWorkspaceHub 'br/public:avm/res/network/private-dns-zone:0.3.1' = [ +// for zone in objectKeys(mlPrivateDnsZones): if (virtualNetworkEnabled && aiFoundryAiHubEnabled) { +// name: take('avm.res.network.private-dns-zone.ai-hub.${uniqueString(aiFoundryAiHubName,zone)}.${solutionPrefix}', 64) +// params: { +// name: zone +// enableTelemetry: enableTelemetry +// tags: tags +// virtualNetworkLinks: [ +// { +// name: 'vnetlink-${split(zone, '.')[1]}' +// virtualNetworkResourceId: virtualNetwork.outputs.resourceId +// } +// ] +// } +// } +// ] +// var aiFoundryAiHubEnabled = aiFoundryAiHubConfiguration.?enabled ?? true +// var aiFoundryAiHubName = aiFoundryAiHubConfiguration.?name ?? 'aih-${solutionPrefix}' +// module aiFoundryAiHub 'modules/ai-hub.bicep' = if (aiFoundryAiHubEnabled) { +// name: take('module.ai-hub.${aiFoundryAiHubName}', 64) +// dependsOn: [ +// privateDnsZonesAiFoundryWorkspaceHub +// ] +// params: { +// name: aiFoundryAiHubName +// location: aiFoundryAiHubConfiguration.?location ?? azureOpenAILocation +// tags: aiFoundryAiHubConfiguration.?tags ?? tags +// sku: aiFoundryAiHubConfiguration.?sku ?? 'Basic' +// aiFoundryAiServicesName: aiFoundryAiServices.outputs.name +// applicationInsightsResourceId: applicationInsights.outputs.resourceId +// enableTelemetry: enableTelemetry +// logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceId +// storageAccountResourceId: aiFoundryStorageAccount.outputs.resourceId +// virtualNetworkEnabled: virtualNetworkEnabled +// privateEndpoints: virtualNetworkEnabled +// ? [ +// { +// name: 'pep-${aiFoundryAiHubName}' +// customNetworkInterfaceName: 'nic-${aiFoundryAiHubName}' +// service: mlTargetSubResource +// subnetResourceId: virtualNetworkEnabled +// ? aiFoundryAiHubConfiguration.?subnetResourceId ?? virtualNetwork.?outputs.?subnetResourceIds[0] +// : null +// privateDnsZoneGroup: { +// privateDnsZoneGroupConfigs: map(objectKeys(mlPrivateDnsZones), zone => { +// name: replace(zone, '.', '-') +// privateDnsZoneResourceId: resourceId('Microsoft.Network/privateDnsZones', zone) +// }) +// } +// } +// ] +// : [] +// } +// } + +// AI Foundry: AI Project +// WAF best practices for Open AI: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-openai +// var aiFoundryAiProjectEnabled = aiFoundryAiProjectConfiguration.?enabled ?? true +var aiFoundryAiProjectName = aiFoundryAiProjectConfiguration.?name ?? 'aifp-${solutionPrefix}' + +// module aiFoundryAiProject 'br/public:avm/res/machine-learning-services/workspace:0.12.0' = if (aiFoundryAiProjectEnabled) { +// name: take('avm.res.machine-learning-services.workspace.${aiFoundryAiProjectName}', 64) +// params: { +// name: aiFoundryAiProjectName +// location: aiFoundryAiProjectConfiguration.?location ?? azureOpenAILocation +// tags: aiFoundryAiProjectConfiguration.?tags ?? tags +// enableTelemetry: enableTelemetry +// diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] +// sku: aiFoundryAiProjectConfiguration.?sku ?? 'Basic' +// kind: 'Project' +// hubResourceId: aiFoundryAiHub.outputs.resourceId +// roleAssignments: [ +// { +// principalId: containerApp.outputs.?systemAssignedMIPrincipalId! +// // Assigning the role with the role name instead of the role ID freezes the deployment at this point +// roleDefinitionIdOrName: '64702f94-c441-49e6-a78b-ef80e0188fee' //'Azure AI Developer' +// } +// ] +// } +// } + +resource aiServices 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { + name: aiFoundryAiServicesResourceName } -module privateDnsZonesAiFoundryStorageAccount 'br/public:avm/res/network/private-dns-zone:0.3.1' = [ - for zone in objectKeys(storageAccountPrivateDnsZones): if (virtualNetworkEnabled && aiFoundryStorageAccountEnabled) { - name: take( - 'avm.res.network.private-dns-zone.storage-account.${uniqueString(aiFoundryStorageAccountResourceName,zone)}.${solutionPrefix}', - 64 - ) - params: { - name: zone - tags: tags - enableTelemetry: enableTelemetry - virtualNetworkLinks: [ - { - name: 'vnetlink-${split(zone, '.')[1]}' - virtualNetworkResourceId: virtualNetwork.outputs.resourceId - } - ] - } +var aiProjectDescription = 'AI Foundry Project' + +resource aiFoundryProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { + parent: aiServices + name: aiFoundryAiProjectName + location: aiFoundryAiProjectConfiguration.?location ?? azureOpenAILocation + identity: { + type: 'SystemAssigned' } -] -var aiFoundryStorageAccountEnabled = aiFoundryStorageAccountConfiguration.?enabled ?? true -var aiFoundryStorageAccountResourceName = aiFoundryStorageAccountConfiguration.?name ?? replace( - 'sthub${solutionPrefix}', - '-', - '' -) - -module aiFoundryStorageAccount 'br/public:avm/res/storage/storage-account:0.18.2' = if (aiFoundryStorageAccountEnabled) { - name: take('avm.res.storage.storage-account.${aiFoundryStorageAccountResourceName}', 64) - dependsOn: [ - privateDnsZonesAiFoundryStorageAccount - ] - params: { - name: aiFoundryStorageAccountResourceName - location: aiFoundryStorageAccountConfiguration.?location ?? azureOpenAILocation - tags: aiFoundryStorageAccountConfiguration.?tags ?? tags - enableTelemetry: enableTelemetry - diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] - skuName: aiFoundryStorageAccountConfiguration.?sku ?? 'Standard_ZRS' - allowSharedKeyAccess: false - networkAcls: { - bypass: 'AzureServices' - defaultAction: 'Allow' - } - blobServices: { - deleteRetentionPolicyEnabled: false - containerDeleteRetentionPolicyDays: 7 - containerDeleteRetentionPolicyEnabled: false - diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] - } - publicNetworkAccess: virtualNetworkEnabled ? 'Disabled' : 'Enabled' - allowBlobPublicAccess: false - privateEndpoints: virtualNetworkEnabled - ? map(items(storageAccountPrivateDnsZones), zone => { - name: 'pep-${zone.value}-${aiFoundryStorageAccountResourceName}' - customNetworkInterfaceName: 'nic-${zone.value}-${aiFoundryStorageAccountResourceName}' - service: zone.value - subnetResourceId: aiFoundryStorageAccountConfiguration.?subnetResourceId ?? virtualNetwork.outputs.subnetResourceIds[0] ?? '' - privateDnsZoneResourceIds: [resourceId('Microsoft.Network/privateDnsZones', zone.key)] - }) - : null - roleAssignments: [ - { - principalId: userAssignedIdentity.outputs.principalId - roleDefinitionIdOrName: 'Storage Blob Data Contributor' - } - ] + properties: { + description: aiProjectDescription + displayName: aiFoundryAiProjectName } } -// AI Foundry: AI Hub -// WAF best practices for Open AI: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-openai -var mlTargetSubResource = 'amlworkspace' -var mlPrivateDnsZones = { - 'privatelink.api.azureml.ms': mlTargetSubResource - 'privatelink.notebooks.azure.net': mlTargetSubResource +resource aiUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: '53ca6127-db72-4b80-b1b0-d745d6d5456d' } -module privateDnsZonesAiFoundryWorkspaceHub 'br/public:avm/res/network/private-dns-zone:0.3.1' = [ - for zone in objectKeys(mlPrivateDnsZones): if (virtualNetworkEnabled && aiFoundryAiHubEnabled) { - name: take('avm.res.network.private-dns-zone.ai-hub.${uniqueString(aiFoundryAiHubName,zone)}.${solutionPrefix}', 64) - params: { - name: zone - enableTelemetry: enableTelemetry - tags: tags - virtualNetworkLinks: [ - { - name: 'vnetlink-${split(zone, '.')[1]}' - virtualNetworkResourceId: virtualNetwork.outputs.resourceId - } - ] - } - } -] -var aiFoundryAiHubEnabled = aiFoundryAiHubConfiguration.?enabled ?? true -var aiFoundryAiHubName = aiFoundryAiHubConfiguration.?name ?? 'aih-${solutionPrefix}' -module aiFoundryAiHub 'modules/ai-hub.bicep' = if (aiFoundryAiHubEnabled) { - name: take('module.ai-hub.${aiFoundryAiHubName}', 64) - dependsOn: [ - privateDnsZonesAiFoundryWorkspaceHub - ] - params: { - name: aiFoundryAiHubName - location: aiFoundryAiHubConfiguration.?location ?? azureOpenAILocation - tags: aiFoundryAiHubConfiguration.?tags ?? tags - sku: aiFoundryAiHubConfiguration.?sku ?? 'Basic' - aiFoundryAiServicesName: aiFoundryAiServices.outputs.name - applicationInsightsResourceId: applicationInsights.outputs.resourceId - enableTelemetry: enableTelemetry - logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceId - storageAccountResourceId: aiFoundryStorageAccount.outputs.resourceId - virtualNetworkEnabled: virtualNetworkEnabled - privateEndpoints: virtualNetworkEnabled - ? [ - { - name: 'pep-${aiFoundryAiHubName}' - customNetworkInterfaceName: 'nic-${aiFoundryAiHubName}' - service: mlTargetSubResource - subnetResourceId: virtualNetworkEnabled - ? aiFoundryAiHubConfiguration.?subnetResourceId ?? virtualNetwork.?outputs.?subnetResourceIds[0] - : null - privateDnsZoneGroup: { - privateDnsZoneGroupConfigs: map(objectKeys(mlPrivateDnsZones), zone => { - name: replace(zone, '.', '-') - privateDnsZoneResourceId: resourceId('Microsoft.Network/privateDnsZones', zone) - }) - } - } - ] - : [] + +resource aiUserAccessProj 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(containerApp.name, aiFoundryProject.id, aiUser.id) + scope: aiFoundryProject + properties: { + roleDefinitionId: aiUser.id + principalId: containerApp.outputs.?systemAssignedMIPrincipalId! } } -// AI Foundry: AI Project -// WAF best practices for Open AI: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-openai -var aiFoundryAiProjectEnabled = aiFoundryAiProjectConfiguration.?enabled ?? true -var aiFoundryAiProjectName = aiFoundryAiProjectConfiguration.?name ?? 'aihb-${solutionPrefix}' +resource aiDeveloper 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: '64702f94-c441-49e6-a78b-ef80e0188fee' +} -module aiFoundryAiProject 'br/public:avm/res/machine-learning-services/workspace:0.12.0' = if (aiFoundryAiProjectEnabled) { - name: take('avm.res.machine-learning-services.workspace.${aiFoundryAiProjectName}', 64) - params: { - name: aiFoundryAiProjectName - location: aiFoundryAiProjectConfiguration.?location ?? azureOpenAILocation - tags: aiFoundryAiProjectConfiguration.?tags ?? tags - enableTelemetry: enableTelemetry - diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] - sku: aiFoundryAiProjectConfiguration.?sku ?? 'Basic' - kind: 'Project' - hubResourceId: aiFoundryAiHub.outputs.resourceId - roleAssignments: [ - { - principalId: containerApp.outputs.?systemAssignedMIPrincipalId! - // Assigning the role with the role name instead of the role ID freezes the deployment at this point - roleDefinitionIdOrName: '64702f94-c441-49e6-a78b-ef80e0188fee' //'Azure AI Developer' - } - ] +resource aiUserAccessFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(containerApp.name, aiServices.id, aiDeveloper.id) + scope: aiFoundryProject + properties: { + roleDefinitionId: aiDeveloper.id + principalId: containerApp.outputs.?systemAssignedMIPrincipalId! } } + // ========== Cosmos DB ========== // // WAF best practices for Cosmos DB: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/cosmos-db module privateDnsZonesCosmosDb 'br/public:avm/res/network/private-dns-zone:0.7.0' = if (virtualNetworkEnabled) { @@ -1189,11 +1244,11 @@ module containerApp 'br/public:avm/res/app/container-app:0.14.2' = if (container name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' value: applicationInsights.outputs.connectionString } - { - name: 'AZURE_AI_AGENT_PROJECT_CONNECTION_STRING' - value: '${toLower(replace(azureOpenAILocation,' ',''))}.api.azureml.ms;${subscription().subscriptionId};${resourceGroup().name};${aiFoundryAiProjectName}' - //Location should be the AI Foundry AI Project location - } + // { + // name: 'AZURE_AI_AGENT_PROJECT_CONNECTION_STRING' + // value: '${toLower(replace(azureOpenAILocation,' ',''))}.api.azureml.ms;${subscription().subscriptionId};${resourceGroup().name};${aiFoundryAiProjectName}' + // //Location should be the AI Foundry AI Project location + // } { name: 'AZURE_AI_SUBSCRIPTION_ID' value: subscription().subscriptionId @@ -1210,6 +1265,14 @@ module containerApp 'br/public:avm/res/app/container-app:0.14.2' = if (container name: 'FRONTEND_SITE_NAME' value: 'https://${webSiteName}.azurewebsites.net' } + { + name: 'AZURE_AI_AGENT_ENDPOINT' + value: aiFoundryProject.properties.endpoints['AI Foundry API'] + } + { + name: 'AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME' + value: aiFoundryAiServicesModelDeployment.name + } ] } ] @@ -1722,29 +1785,29 @@ type aiServicesConfigurationType = { modelCapacity: int? } -@export() -@description('The type for the Multi-Agent Custom Automation Engine Storage Account resource configuration.') -type storageAccountType = { - @description('Optional. If the Storage Account resource should be deployed or not.') - enabled: bool? +// @export() +// @description('The type for the Multi-Agent Custom Automation Engine Storage Account resource configuration.') +// type storageAccountType = { +// @description('Optional. If the Storage Account resource should be deployed or not.') +// enabled: bool? - @description('Optional. The name of the Storage Account resource.') - @maxLength(60) - name: string? +// @description('Optional. The name of the Storage Account resource.') +// @maxLength(60) +// name: string? - @description('Optional. Location for the Storage Account resource.') - @metadata({ azd: { type: 'location' } }) - location: string? +// @description('Optional. Location for the Storage Account resource.') +// @metadata({ azd: { type: 'location' } }) +// location: string? - @description('Optional. The tags to set for the Storage Account resource.') - tags: object? +// @description('Optional. The tags to set for the Storage Account resource.') +// tags: object? - @description('Optional. The SKU for the Storage Account resource.') - sku: ('Standard_LRS' | 'Standard_GRS' | 'Standard_RAGRS' | 'Standard_ZRS' | 'Premium_LRS' | 'Premium_ZRS')? +// @description('Optional. The SKU for the Storage Account resource.') +// sku: ('Standard_LRS' | 'Standard_GRS' | 'Standard_RAGRS' | 'Standard_ZRS' | 'Premium_LRS' | 'Premium_ZRS')? - @description('Optional. The resource Id of the subnet where the Storage Account private endpoint should be created.') - subnetResourceId: string? -} +// @description('Optional. The resource Id of the subnet where the Storage Account private endpoint should be created.') +// subnetResourceId: string? +// } @export() @description('The type for the Multi-Agent Custom Automation Engine AI Hub resource configuration.') diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 5f32de008..60a29c75c 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -18,9 +18,9 @@ param applicationInsightsConfiguration = { param virtualNetworkConfiguration = { enabled: false } -param aiFoundryStorageAccountConfiguration = { - sku: 'Standard_LRS' -} +// param aiFoundryStorageAccountConfiguration = { +// sku: 'Standard_LRS' +// } param webServerFarmConfiguration = { skuCapacity: 1 skuName: 'B2' From dd786666fa7e60005630f028f0b88b5429dcb7e7 Mon Sep 17 00:00:00 2001 From: Abdul-Microsoft Date: Fri, 13 Jun 2025 17:49:09 +0530 Subject: [PATCH 04/16] role assignment changes --- infra/main.bicep | 60 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index 37c56a591..0becd76c6 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -791,23 +791,23 @@ module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.11.0' } ]) : [] - roleAssignments: [ - // { - // principalId: userAssignedIdentity.outputs.principalId - // principalType: 'ServicePrincipal' - // roleDefinitionIdOrName: 'Cognitive Services OpenAI User' - // } - { - principalId: containerApp.outputs.?systemAssignedMIPrincipalId! - principalType: 'ServicePrincipal' - roleDefinitionIdOrName: 'Cognitive Services OpenAI User' - } - { - principalId: containerApp.outputs.?systemAssignedMIPrincipalId! - principalType: 'ServicePrincipal' - roleDefinitionIdOrName: '53ca6127-db72-4b80-b1b0-d745d6d5456d' - } - ] + // roleAssignments: [ + // // { + // // principalId: userAssignedIdentity.outputs.principalId + // // principalType: 'ServicePrincipal' + // // roleDefinitionIdOrName: 'Cognitive Services OpenAI User' + // // } + // { + // principalId: containerApp.outputs.?systemAssignedMIPrincipalId! + // principalType: 'ServicePrincipal' + // roleDefinitionIdOrName: 'Cognitive Services OpenAI User' + // } + // { + // principalId: containerApp.outputs.?systemAssignedMIPrincipalId! + // principalType: 'ServicePrincipal' + // roleDefinitionIdOrName: '53ca6127-db72-4b80-b1b0-d745d6d5456d' + // } + // ] deployments: aiFoundryAiServicesConfiguration.?deployments ?? [ { name: aiFoundryAiServicesModelDeployment.name @@ -1007,6 +1007,9 @@ resource aiFoundryProject 'Microsoft.CognitiveServices/accounts/projects@2025-04 description: aiProjectDescription displayName: aiFoundryAiProjectName } + dependsOn:[ + aiServices + ] } resource aiUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { @@ -1022,11 +1025,20 @@ resource aiUserAccessProj 'Microsoft.Authorization/roleAssignments@2022-04-01' = } } +resource aiUserAccessFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(containerApp.name, aiServices.id, aiUser.id) + scope: aiServices + properties: { + roleDefinitionId: aiUser.id + principalId: containerApp.outputs.?systemAssignedMIPrincipalId! + } +} + resource aiDeveloper 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { name: '64702f94-c441-49e6-a78b-ef80e0188fee' } -resource aiUserAccessFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01' = { +resource aiDeveloperAccessFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(containerApp.name, aiServices.id, aiDeveloper.id) scope: aiFoundryProject properties: { @@ -1035,6 +1047,18 @@ resource aiUserAccessFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01 } } +resource CognitiveServiceOpenAIUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' +} + +resource cognitiveServiceOpenAIUserAccessFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(containerApp.name, aiServices.id, CognitiveServiceOpenAIUser.id) + scope: aiServices + properties: { + roleDefinitionId: CognitiveServiceOpenAIUser.id + principalId: containerApp.outputs.?systemAssignedMIPrincipalId! + } +} // ========== Cosmos DB ========== // // WAF best practices for Cosmos DB: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/cosmos-db From 8f00a7309fe45733bdf5498f2fc5b954cf1557a7 Mon Sep 17 00:00:00 2001 From: Abdul-Microsoft Date: Fri, 13 Jun 2025 19:33:24 +0530 Subject: [PATCH 05/16] added dependson to fix aiservice deployment issue --- infra/main.bicep | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index 0becd76c6..2d45b3c8a 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -992,6 +992,9 @@ var aiFoundryAiProjectName = aiFoundryAiProjectConfiguration.?name ?? 'aifp-${so resource aiServices 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { name: aiFoundryAiServicesResourceName + dependsOn:[ + aiFoundryAiServices + ] } var aiProjectDescription = 'AI Foundry Project' @@ -1007,9 +1010,6 @@ resource aiFoundryProject 'Microsoft.CognitiveServices/accounts/projects@2025-04 description: aiProjectDescription displayName: aiFoundryAiProjectName } - dependsOn:[ - aiServices - ] } resource aiUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { @@ -1268,11 +1268,11 @@ module containerApp 'br/public:avm/res/app/container-app:0.14.2' = if (container name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' value: applicationInsights.outputs.connectionString } - // { - // name: 'AZURE_AI_AGENT_PROJECT_CONNECTION_STRING' - // value: '${toLower(replace(azureOpenAILocation,' ',''))}.api.azureml.ms;${subscription().subscriptionId};${resourceGroup().name};${aiFoundryAiProjectName}' - // //Location should be the AI Foundry AI Project location - // } + { + name: 'AZURE_AI_AGENT_PROJECT_CONNECTION_STRING' + value: '${toLower(replace(azureOpenAILocation,' ',''))}.api.azureml.ms;${subscription().subscriptionId};${resourceGroup().name};${aiFoundryAiProjectName}' + //Location should be the AI Foundry AI Project location + } { name: 'AZURE_AI_SUBSCRIPTION_ID' value: subscription().subscriptionId From b711fc259fb290fb77006b06c54ff85234676dc8 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Fri, 13 Jun 2025 20:20:39 +0530 Subject: [PATCH 06/16] sample.env file --- src/backend/.env.sample | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/backend/.env.sample b/src/backend/.env.sample index ddc1103d3..15bb1e0d4 100644 --- a/src/backend/.env.sample +++ b/src/backend/.env.sample @@ -9,13 +9,13 @@ AZURE_OPENAI_API_VERSION=2024-08-01-preview APPLICATIONINSIGHTS_INSTRUMENTATION_KEY= AZURE_AI_PROJECT_ENDPOINT= -AZURE_AI_SUBSCRIPTION_ID= +AZURE_AI_SUBSCRIPTION_ID=1 AZURE_AI_RESOURCE_GROUP= AZURE_AI_PROJECT_NAME= -AZURE_AI_AGENT_PROJECT_CONNECTION_STRING= AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o -APPLICATIONINSIGHTS_CONNECTION_STRING= +AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME=gpt-4o +#APPLICATIONINSIGHTS_CONNECTION_STRING= +AZURE_AI_AGENT_PROJECT_ENDPOINT= - -BACKEND_API_URL='http://localhost:8000' -FRONTEND_SITE_NAME='http://127.0.0.1:3000' \ No newline at end of file +BACKEND_API_URL=http://localhost:8000 +FRONTEND_SITE_NAME=http://127.0.0.1:3000 \ No newline at end of file From f413121e3c8ea36ebec33942cc5c9cca0ea086de Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Fri, 13 Jun 2025 20:44:43 +0530 Subject: [PATCH 07/16] FDP-Backend_Changes --- src/backend/app_config.py | 10 ++-- src/backend/app_kernel.py | 20 ++++---- src/backend/config_kernel.py | 4 +- src/backend/kernel_agents/agent_base.py | 3 +- src/backend/kernel_agents/agent_factory.py | 13 ++---- src/backend/kernel_agents/planner_agent.py | 54 ++++++++++++---------- src/backend/pyproject.toml | 2 +- src/backend/requirements.txt | 6 +-- 8 files changed, 53 insertions(+), 59 deletions(-) diff --git a/src/backend/app_config.py b/src/backend/app_config.py index 798a510d2..7383018be 100644 --- a/src/backend/app_config.py +++ b/src/backend/app_config.py @@ -49,9 +49,7 @@ def __init__(self): self.AZURE_AI_SUBSCRIPTION_ID = self._get_required("AZURE_AI_SUBSCRIPTION_ID") self.AZURE_AI_RESOURCE_GROUP = self._get_required("AZURE_AI_RESOURCE_GROUP") self.AZURE_AI_PROJECT_NAME = self._get_required("AZURE_AI_PROJECT_NAME") - self.AZURE_AI_AGENT_PROJECT_CONNECTION_STRING = self._get_required( - "AZURE_AI_AGENT_PROJECT_CONNECTION_STRING" - ) + self.AZURE_AI_AGENT_PROJECT_ENDPOINT = self._get_required("AZURE_AI_AGENT_PROJECT_ENDPOINT") # Cached clients and resources self._azure_credentials = None @@ -177,10 +175,8 @@ def get_ai_project_client(self): "Unable to acquire Azure credentials; ensure DefaultAzureCredential is configured" ) - connection_string = self.AZURE_AI_AGENT_PROJECT_CONNECTION_STRING - self._ai_project_client = AIProjectClient.from_connection_string( - credential=credential, conn_str=connection_string - ) + endpoint = self.AZURE_AI_AGENT_PROJECT_ENDPOINT + self._ai_project_client = AIProjectClient(endpoint=endpoint, credential=credential) return self._ai_project_client except Exception as exc: diff --git a/src/backend/app_kernel.py b/src/backend/app_kernel.py index a1ba44567..3d504e2b3 100644 --- a/src/backend/app_kernel.py +++ b/src/backend/app_kernel.py @@ -35,18 +35,18 @@ from utils_kernel import initialize_runtime_and_context, rai_success # Check if the Application Insights Instrumentation Key is set in the environment variables -connection_string = os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING") -if connection_string: +#connection_string = os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING") +#if connection_string: # Configure Application Insights if the Instrumentation Key is found #configure_azure_monitor(connection_string=connection_string) - logging.info( - "Application Insights configured with the provided Instrumentation Key" - ) -else: - # Log a warning if the Instrumentation Key is not found - logging.warning( - "No Application Insights Instrumentation Key found. Skipping configuration" - ) +# logging.info( +# "Application Insights configured with the provided Instrumentation Key" +# ) +#else: +# # Log a warning if the Instrumentation Key is not found +# logging.warning( +# "No Application Insights Instrumentation Key found. Skipping configuration" +# ) # Configure logging logging.basicConfig(level=logging.INFO) diff --git a/src/backend/config_kernel.py b/src/backend/config_kernel.py index 6227292dd..d1b41e6ed 100644 --- a/src/backend/config_kernel.py +++ b/src/backend/config_kernel.py @@ -26,9 +26,7 @@ class Config: AZURE_AI_SUBSCRIPTION_ID = config.AZURE_AI_SUBSCRIPTION_ID AZURE_AI_RESOURCE_GROUP = config.AZURE_AI_RESOURCE_GROUP AZURE_AI_PROJECT_NAME = config.AZURE_AI_PROJECT_NAME - AZURE_AI_AGENT_PROJECT_CONNECTION_STRING = ( - config.AZURE_AI_AGENT_PROJECT_CONNECTION_STRING - ) + AZURE_AI_AGENT_PROJECT_ENDPOINT = config.AZURE_AI_AGENT_PROJECT_ENDPOINT @staticmethod def GetAzureCredentials(): diff --git a/src/backend/kernel_agents/agent_base.py b/src/backend/kernel_agents/agent_base.py index 9abab5fbe..6a6c9bfa0 100644 --- a/src/backend/kernel_agents/agent_base.py +++ b/src/backend/kernel_agents/agent_base.py @@ -8,7 +8,8 @@ from event_utils import track_event_if_configured from models.messages_kernel import (ActionRequest, ActionResponse, AgentMessage, Step, StepStatus) -from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent +from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent, AzureAIAgentThread +from semantic_kernel.agents.azure_ai.azure_ai_agent_settings import AzureAIAgentSettings from semantic_kernel.functions import KernelFunction # Default formatting instructions used across agents diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index 440fa2058..535e79340 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -6,7 +6,7 @@ # Import the new AppConfig instance from app_config import config -from azure.ai.projects.models import (ResponseFormatJsonSchema, +from azure.ai.agents.models import (ResponseFormatJsonSchema, ResponseFormatJsonSchemaType) from context.cosmos_memory_kernel import CosmosMemoryContext from kernel_agents.agent_base import BaseAgent @@ -22,8 +22,8 @@ from kernel_agents.tech_support_agent import TechSupportAgent from models.messages_kernel import AgentType, PlannerResponsePlan # pylint:disable=E0611 -from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent - +from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent, AzureAIAgentThread +from semantic_kernel.agents.azure_ai.azure_ai_agent_settings import AzureAIAgentSettings logger = logging.getLogger(__name__) @@ -265,13 +265,6 @@ async def create_all_agents( temperature=temperature, agent_instances=agent_instances, # Pass agent instances to the planner client=client, - response_format=ResponseFormatJsonSchemaType( - json_schema=ResponseFormatJsonSchema( - name=PlannerResponsePlan.__name__, - description=f"respond with {PlannerResponsePlan.__name__.lower()}", - schema=PlannerResponsePlan.model_json_schema(), - ) - ), ) agent_instances[AgentType.PLANNER.value] = ( planner_agent # to pass it to group chat manager diff --git a/src/backend/kernel_agents/planner_agent.py b/src/backend/kernel_agents/planner_agent.py index 2bc5ad5b8..94105878e 100644 --- a/src/backend/kernel_agents/planner_agent.py +++ b/src/backend/kernel_agents/planner_agent.py @@ -3,7 +3,7 @@ import uuid from typing import Any, Dict, List, Optional, Tuple -from azure.ai.projects.models import (ResponseFormatJsonSchema, +from azure.ai.agents.models import (ResponseFormatJsonSchema, ResponseFormatJsonSchemaType) from context.cosmos_memory_kernel import CosmosMemoryContext from event_utils import track_event_if_configured @@ -133,37 +133,43 @@ async def create( try: logging.info("Initializing PlannerAgent from async init azure AI Agent") + # Construct response_format with the required 'type' field + response_format = { + "type": "json_schema", # Add the missing type field + "json_schema": { + "name": PlannerResponsePlan.__name__, + "description": f"respond with {PlannerResponsePlan.__name__.lower()}", + "schema": PlannerResponsePlan.model_json_schema() + } + } + # Create the Azure AI Agent using AppConfig with string instructions agent_definition = await cls._create_azure_ai_agent_definition( agent_name=agent_name, - instructions=cls._get_template(), # Pass the formatted string, not an object + instructions=cls._get_template(), # Pass the formatted string temperature=0.0, - response_format=ResponseFormatJsonSchemaType( - json_schema=ResponseFormatJsonSchema( - name=PlannerResponsePlan.__name__, - description=f"respond with {PlannerResponsePlan.__name__.lower()}", - schema=PlannerResponsePlan.model_json_schema(), - ) - ), + response_format=response_format, ) - return cls( - session_id=session_id, - user_id=user_id, - memory_store=memory_store, - tools=tools, - system_message=system_message, - agent_name=agent_name, - available_agents=available_agents, - agent_instances=agent_instances, - client=client, - definition=agent_definition, - ) - - except Exception as e: - logging.error(f"Failed to create Azure AI Agent for PlannerAgent: {e}") + except Exception as exc: + logging.error("Error initializing PlannerAgent definition: %s", exc) raise + return cls( + session_id=session_id, + user_id=user_id, + memory_store=memory_store, + tools=tools, + system_message=system_message, + agent_name=agent_name, + available_agents=available_agents, + agent_instances=agent_instances, + client=client, + definition=agent_definition, + ) + + + async def handle_input_task(self, input_task: InputTask) -> str: """Handle the initial input task from the user. diff --git a/src/backend/pyproject.toml b/src/backend/pyproject.toml index b989b2f14..e02186fdb 100644 --- a/src/backend/pyproject.toml +++ b/src/backend/pyproject.toml @@ -26,6 +26,6 @@ dependencies = [ "pytest-cov==5.0.0", "python-dotenv>=1.1.0", "python-multipart>=0.0.20", - "semantic-kernel>=1.28.1", + "semantic-kernel>=1.32.2", "uvicorn>=0.34.2", ] diff --git a/src/backend/requirements.txt b/src/backend/requirements.txt index 76568fc86..5cac25b2f 100644 --- a/src/backend/requirements.txt +++ b/src/backend/requirements.txt @@ -14,9 +14,9 @@ opentelemetry-instrumentation-fastapi opentelemetry-instrumentation-openai opentelemetry-exporter-otlp-proto-http -semantic-kernel[azure]==1.28.1 -azure-ai-projects==1.0.0b10 -openai +semantic-kernel[azure]==1.32.2 +azure-ai-projects==1.0.0b11 +openai==1.84.0 azure-ai-inference==1.0.0b9 azure-search-documents azure-ai-evaluation From a1e385568826e66ec35a340bad9b3a19f04051b9 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Fri, 13 Jun 2025 20:58:34 +0530 Subject: [PATCH 08/16] Agent_factory Updated --- src/backend/kernel_agents/agent_factory.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index 535e79340..d9c110a68 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -265,6 +265,14 @@ async def create_all_agents( temperature=temperature, agent_instances=agent_instances, # Pass agent instances to the planner client=client, + response_format = { + "type": "json_schema", + "json_schema": { + "name": PlannerResponsePlan.__name__, + "description": f"respond with {PlannerResponsePlan.__name__.lower()}", + "schema": PlannerResponsePlan.model_json_schema() + } + }, ) agent_instances[AgentType.PLANNER.value] = ( planner_agent # to pass it to group chat manager From ba6c85beabdb4b8b6698335a742780b44e710f38 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Fri, 13 Jun 2025 23:39:24 +0530 Subject: [PATCH 09/16] Endpoint Updated --- src/backend/.env.sample | 2 +- src/backend/app_config.py | 4 ++-- src/backend/config_kernel.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/backend/.env.sample b/src/backend/.env.sample index 15bb1e0d4..970709a56 100644 --- a/src/backend/.env.sample +++ b/src/backend/.env.sample @@ -15,7 +15,7 @@ AZURE_AI_PROJECT_NAME= AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME=gpt-4o #APPLICATIONINSIGHTS_CONNECTION_STRING= -AZURE_AI_AGENT_PROJECT_ENDPOINT= +AZURE_AI_AGENT_ENDPOINT= BACKEND_API_URL=http://localhost:8000 FRONTEND_SITE_NAME=http://127.0.0.1:3000 \ No newline at end of file diff --git a/src/backend/app_config.py b/src/backend/app_config.py index 7383018be..d4b1a9e9a 100644 --- a/src/backend/app_config.py +++ b/src/backend/app_config.py @@ -49,7 +49,7 @@ def __init__(self): self.AZURE_AI_SUBSCRIPTION_ID = self._get_required("AZURE_AI_SUBSCRIPTION_ID") self.AZURE_AI_RESOURCE_GROUP = self._get_required("AZURE_AI_RESOURCE_GROUP") self.AZURE_AI_PROJECT_NAME = self._get_required("AZURE_AI_PROJECT_NAME") - self.AZURE_AI_AGENT_PROJECT_ENDPOINT = self._get_required("AZURE_AI_AGENT_PROJECT_ENDPOINT") + self.AZURE_AI_AGENT_ENDPOINT = self._get_required("AZURE_AI_AGENT_ENDPOINT") # Cached clients and resources self._azure_credentials = None @@ -175,7 +175,7 @@ def get_ai_project_client(self): "Unable to acquire Azure credentials; ensure DefaultAzureCredential is configured" ) - endpoint = self.AZURE_AI_AGENT_PROJECT_ENDPOINT + endpoint = self.AZURE_AI_AGENT_ENDPOINT self._ai_project_client = AIProjectClient(endpoint=endpoint, credential=credential) return self._ai_project_client diff --git a/src/backend/config_kernel.py b/src/backend/config_kernel.py index d1b41e6ed..80d0738af 100644 --- a/src/backend/config_kernel.py +++ b/src/backend/config_kernel.py @@ -26,7 +26,7 @@ class Config: AZURE_AI_SUBSCRIPTION_ID = config.AZURE_AI_SUBSCRIPTION_ID AZURE_AI_RESOURCE_GROUP = config.AZURE_AI_RESOURCE_GROUP AZURE_AI_PROJECT_NAME = config.AZURE_AI_PROJECT_NAME - AZURE_AI_AGENT_PROJECT_ENDPOINT = config.AZURE_AI_AGENT_PROJECT_ENDPOINT + AZURE_AI_AGENT_ENDPOINT = config.AZURE_AI_AGENT_ENDPOINT @staticmethod def GetAzureCredentials(): From 414ae4739f367f49ea8695d694933ee00cec7a73 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Fri, 13 Jun 2025 23:43:10 +0530 Subject: [PATCH 10/16] env changes --- src/backend/.env.sample | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/.env.sample b/src/backend/.env.sample index 970709a56..a441829c4 100644 --- a/src/backend/.env.sample +++ b/src/backend/.env.sample @@ -9,7 +9,7 @@ AZURE_OPENAI_API_VERSION=2024-08-01-preview APPLICATIONINSIGHTS_INSTRUMENTATION_KEY= AZURE_AI_PROJECT_ENDPOINT= -AZURE_AI_SUBSCRIPTION_ID=1 +AZURE_AI_SUBSCRIPTION_ID= AZURE_AI_RESOURCE_GROUP= AZURE_AI_PROJECT_NAME= AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o From 1b38395f49c323d58a2de92238439adb9dfb0911 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Mon, 16 Jun 2025 13:06:21 +0530 Subject: [PATCH 11/16] Pylint Issue --- src/backend/app_kernel.py | 28 +++++++++++----------- src/backend/kernel_agents/agent_base.py | 3 +-- src/backend/kernel_agents/agent_factory.py | 4 ++-- src/backend/kernel_agents/planner_agent.py | 3 --- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/backend/app_kernel.py b/src/backend/app_kernel.py index 3d504e2b3..b7b22f07c 100644 --- a/src/backend/app_kernel.py +++ b/src/backend/app_kernel.py @@ -1,7 +1,6 @@ # app_kernel.py import asyncio import logging -import os import uuid from typing import Dict, List, Optional @@ -10,7 +9,7 @@ from auth.auth_utils import get_authenticated_user_details # Azure monitoring -from azure.monitor.opentelemetry import configure_azure_monitor +# from azure.monitor.opentelemetry import configure_azure_monitor from config_kernel import Config from event_utils import track_event_if_configured @@ -35,22 +34,23 @@ from utils_kernel import initialize_runtime_and_context, rai_success # Check if the Application Insights Instrumentation Key is set in the environment variables -#connection_string = os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING") -#if connection_string: - # Configure Application Insights if the Instrumentation Key is found - #configure_azure_monitor(connection_string=connection_string) -# logging.info( -# "Application Insights configured with the provided Instrumentation Key" -# ) -#else: -# # Log a warning if the Instrumentation Key is not found -# logging.warning( -# "No Application Insights Instrumentation Key found. Skipping configuration" -# ) +# connection_string = os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING") +# if connection_string: +# # Configure Application Insights if the Instrumentation Key is found +# configure_azure_monitor(connection_string=connection_string) +# logging.info( +# "Application Insights configured with the provided Instrumentation Key" +# ) +# else: +# # Log a warning if the Instrumentation Key is not found +# logging.warning( +# "No Application Insights Instrumentation Key found. Skipping configuration" +# ) # Configure logging logging.basicConfig(level=logging.INFO) + # Suppress INFO logs from 'azure.core.pipeline.policies.http_logging_policy' logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel( logging.WARNING diff --git a/src/backend/kernel_agents/agent_base.py b/src/backend/kernel_agents/agent_base.py index 6a6c9bfa0..9abab5fbe 100644 --- a/src/backend/kernel_agents/agent_base.py +++ b/src/backend/kernel_agents/agent_base.py @@ -8,8 +8,7 @@ from event_utils import track_event_if_configured from models.messages_kernel import (ActionRequest, ActionResponse, AgentMessage, Step, StepStatus) -from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent, AzureAIAgentThread -from semantic_kernel.agents.azure_ai.azure_ai_agent_settings import AzureAIAgentSettings +from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent from semantic_kernel.functions import KernelFunction # Default formatting instructions used across agents diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index d9c110a68..03f1409a4 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -23,7 +23,7 @@ from models.messages_kernel import AgentType, PlannerResponsePlan # pylint:disable=E0611 from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent, AzureAIAgentThread -from semantic_kernel.agents.azure_ai.azure_ai_agent_settings import AzureAIAgentSettings + logger = logging.getLogger(__name__) @@ -265,7 +265,7 @@ async def create_all_agents( temperature=temperature, agent_instances=agent_instances, # Pass agent instances to the planner client=client, - response_format = { + response_format= { "type": "json_schema", "json_schema": { "name": PlannerResponsePlan.__name__, diff --git a/src/backend/kernel_agents/planner_agent.py b/src/backend/kernel_agents/planner_agent.py index 94105878e..a7a21ec7a 100644 --- a/src/backend/kernel_agents/planner_agent.py +++ b/src/backend/kernel_agents/planner_agent.py @@ -3,8 +3,6 @@ import uuid from typing import Any, Dict, List, Optional, Tuple -from azure.ai.agents.models import (ResponseFormatJsonSchema, - ResponseFormatJsonSchemaType) from context.cosmos_memory_kernel import CosmosMemoryContext from event_utils import track_event_if_configured from kernel_agents.agent_base import BaseAgent @@ -21,7 +19,6 @@ from semantic_kernel.functions import KernelFunction from semantic_kernel.functions.kernel_arguments import KernelArguments - class PlannerAgent(BaseAgent): """Planner agent implementation using Semantic Kernel. From ac6240b604acd50ec400b052516df5dbe7eb99e5 Mon Sep 17 00:00:00 2001 From: UtkarshMishra-Microsoft Date: Mon, 16 Jun 2025 13:12:01 +0530 Subject: [PATCH 12/16] Pylint removal --- src/backend/kernel_agents/agent_factory.py | 7 +++---- src/backend/kernel_agents/planner_agent.py | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index 03f1409a4..07f53ae49 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -6,8 +6,7 @@ # Import the new AppConfig instance from app_config import config -from azure.ai.agents.models import (ResponseFormatJsonSchema, - ResponseFormatJsonSchemaType) + from context.cosmos_memory_kernel import CosmosMemoryContext from kernel_agents.agent_base import BaseAgent from kernel_agents.generic_agent import GenericAgent @@ -22,7 +21,7 @@ from kernel_agents.tech_support_agent import TechSupportAgent from models.messages_kernel import AgentType, PlannerResponsePlan # pylint:disable=E0611 -from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent, AzureAIAgentThread +from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent logger = logging.getLogger(__name__) @@ -265,7 +264,7 @@ async def create_all_agents( temperature=temperature, agent_instances=agent_instances, # Pass agent instances to the planner client=client, - response_format= { + response_format={ "type": "json_schema", "json_schema": { "name": PlannerResponsePlan.__name__, diff --git a/src/backend/kernel_agents/planner_agent.py b/src/backend/kernel_agents/planner_agent.py index a7a21ec7a..00f02498a 100644 --- a/src/backend/kernel_agents/planner_agent.py +++ b/src/backend/kernel_agents/planner_agent.py @@ -19,6 +19,7 @@ from semantic_kernel.functions import KernelFunction from semantic_kernel.functions.kernel_arguments import KernelArguments + class PlannerAgent(BaseAgent): """Planner agent implementation using Semantic Kernel. @@ -165,8 +166,6 @@ async def create( definition=agent_definition, ) - - async def handle_input_task(self, input_task: InputTask) -> str: """Handle the initial input task from the user. From 3170e0113ec24531c6a9b318ddd95ce5e1ddc477 Mon Sep 17 00:00:00 2001 From: Abdul-Microsoft Date: Mon, 16 Jun 2025 16:17:40 +0530 Subject: [PATCH 13/16] Update Application Insights configuration and refactor response format handling in agent classes --- src/backend/.env.sample | 2 +- src/backend/app_kernel.py | 29 ++++++------ src/backend/kernel_agents/agent_factory.py | 20 ++++---- src/backend/kernel_agents/planner_agent.py | 54 +++++++++++----------- 4 files changed, 52 insertions(+), 53 deletions(-) diff --git a/src/backend/.env.sample b/src/backend/.env.sample index a441829c4..b0c3738b5 100644 --- a/src/backend/.env.sample +++ b/src/backend/.env.sample @@ -14,7 +14,7 @@ AZURE_AI_RESOURCE_GROUP= AZURE_AI_PROJECT_NAME= AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME=gpt-4o -#APPLICATIONINSIGHTS_CONNECTION_STRING= +APPLICATIONINSIGHTS_CONNECTION_STRING= AZURE_AI_AGENT_ENDPOINT= BACKEND_API_URL=http://localhost:8000 diff --git a/src/backend/app_kernel.py b/src/backend/app_kernel.py index b7b22f07c..6710e400c 100644 --- a/src/backend/app_kernel.py +++ b/src/backend/app_kernel.py @@ -1,6 +1,7 @@ # app_kernel.py import asyncio import logging +import os import uuid from typing import Dict, List, Optional @@ -9,7 +10,7 @@ from auth.auth_utils import get_authenticated_user_details # Azure monitoring -# from azure.monitor.opentelemetry import configure_azure_monitor +from azure.monitor.opentelemetry import configure_azure_monitor from config_kernel import Config from event_utils import track_event_if_configured @@ -33,19 +34,19 @@ # Updated import for KernelArguments from utils_kernel import initialize_runtime_and_context, rai_success -# Check if the Application Insights Instrumentation Key is set in the environment variables -# connection_string = os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING") -# if connection_string: -# # Configure Application Insights if the Instrumentation Key is found -# configure_azure_monitor(connection_string=connection_string) -# logging.info( -# "Application Insights configured with the provided Instrumentation Key" -# ) -# else: -# # Log a warning if the Instrumentation Key is not found -# logging.warning( -# "No Application Insights Instrumentation Key found. Skipping configuration" -# ) +#Check if the Application Insights Instrumentation Key is set in the environment variables +connection_string = os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING") +if connection_string: + # Configure Application Insights if the Instrumentation Key is found + configure_azure_monitor(connection_string=connection_string) + logging.info( + "Application Insights configured with the provided Instrumentation Key" + ) +else: + # Log a warning if the Instrumentation Key is not found + logging.warning( + "No Application Insights Instrumentation Key found. Skipping configuration" + ) # Configure logging logging.basicConfig(level=logging.INFO) diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index 07f53ae49..c098027ea 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -6,7 +6,8 @@ # Import the new AppConfig instance from app_config import config - +from azure.ai.agents.models import (ResponseFormatJsonSchema, + ResponseFormatJsonSchemaType) from context.cosmos_memory_kernel import CosmosMemoryContext from kernel_agents.agent_base import BaseAgent from kernel_agents.generic_agent import GenericAgent @@ -264,14 +265,13 @@ async def create_all_agents( temperature=temperature, agent_instances=agent_instances, # Pass agent instances to the planner client=client, - response_format={ - "type": "json_schema", - "json_schema": { - "name": PlannerResponsePlan.__name__, - "description": f"respond with {PlannerResponsePlan.__name__.lower()}", - "schema": PlannerResponsePlan.model_json_schema() - } - }, + response_format=ResponseFormatJsonSchemaType( + json_schema=ResponseFormatJsonSchema( + name=PlannerResponsePlan.__name__, + description=f"respond with {PlannerResponsePlan.__name__.lower()}", + schema=PlannerResponsePlan.model_json_schema(), + ) + ), ) agent_instances[AgentType.PLANNER.value] = ( planner_agent # to pass it to group chat manager @@ -326,4 +326,4 @@ def clear_cache(cls, session_id: Optional[str] = None) -> None: else: cls._agent_cache.clear() cls._azure_ai_agent_cache.clear() - logger.info("Cleared all agent caches") + logger.info("Cleared all agent caches") \ No newline at end of file diff --git a/src/backend/kernel_agents/planner_agent.py b/src/backend/kernel_agents/planner_agent.py index 00f02498a..e7b7f9117 100644 --- a/src/backend/kernel_agents/planner_agent.py +++ b/src/backend/kernel_agents/planner_agent.py @@ -3,6 +3,8 @@ import uuid from typing import Any, Dict, List, Optional, Tuple +from azure.ai.agents.models import (ResponseFormatJsonSchema, + ResponseFormatJsonSchemaType) from context.cosmos_memory_kernel import CosmosMemoryContext from event_utils import track_event_if_configured from kernel_agents.agent_base import BaseAgent @@ -131,40 +133,36 @@ async def create( try: logging.info("Initializing PlannerAgent from async init azure AI Agent") - # Construct response_format with the required 'type' field - response_format = { - "type": "json_schema", # Add the missing type field - "json_schema": { - "name": PlannerResponsePlan.__name__, - "description": f"respond with {PlannerResponsePlan.__name__.lower()}", - "schema": PlannerResponsePlan.model_json_schema() - } - } - # Create the Azure AI Agent using AppConfig with string instructions agent_definition = await cls._create_azure_ai_agent_definition( agent_name=agent_name, - instructions=cls._get_template(), # Pass the formatted string + instructions=cls._get_template(), # Pass the formatted string, not an object temperature=0.0, - response_format=response_format, + response_format=ResponseFormatJsonSchemaType( + json_schema=ResponseFormatJsonSchema( + name=PlannerResponsePlan.__name__, + description=f"respond with {PlannerResponsePlan.__name__.lower()}", + schema=PlannerResponsePlan.model_json_schema(), + ) + ), ) - except Exception as exc: - logging.error("Error initializing PlannerAgent definition: %s", exc) - raise + return cls( + session_id=session_id, + user_id=user_id, + memory_store=memory_store, + tools=tools, + system_message=system_message, + agent_name=agent_name, + available_agents=available_agents, + agent_instances=agent_instances, + client=client, + definition=agent_definition, + ) - return cls( - session_id=session_id, - user_id=user_id, - memory_store=memory_store, - tools=tools, - system_message=system_message, - agent_name=agent_name, - available_agents=available_agents, - agent_instances=agent_instances, - client=client, - definition=agent_definition, - ) + except Exception as e: + logging.error(f"Failed to create Azure AI Agent for PlannerAgent: {e}") + raise async def handle_input_task(self, input_task: InputTask) -> str: """Handle the initial input task from the user. @@ -598,4 +596,4 @@ def _get_template(): Choose from {{$agents_str}} ONLY for planning your steps. """ - return instruction_template + return instruction_template \ No newline at end of file From 23c469b8fd61e3f4045d5e455d7ac11fbe6cc222 Mon Sep 17 00:00:00 2001 From: Abdul-Microsoft Date: Mon, 16 Jun 2025 17:01:34 +0530 Subject: [PATCH 14/16] resolved pylint and agent recreation issue --- src/backend/app_kernel.py | 7 +++---- src/backend/kernel_agents/agent_base.py | 4 ++-- src/backend/kernel_agents/agent_factory.py | 4 ++-- src/backend/kernel_agents/planner_agent.py | 4 ++-- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/backend/app_kernel.py b/src/backend/app_kernel.py index 6710e400c..217371d20 100644 --- a/src/backend/app_kernel.py +++ b/src/backend/app_kernel.py @@ -10,7 +10,7 @@ from auth.auth_utils import get_authenticated_user_details # Azure monitoring -from azure.monitor.opentelemetry import configure_azure_monitor +# from azure.monitor.opentelemetry import configure_azure_monitor from config_kernel import Config from event_utils import track_event_if_configured @@ -34,11 +34,11 @@ # Updated import for KernelArguments from utils_kernel import initialize_runtime_and_context, rai_success -#Check if the Application Insights Instrumentation Key is set in the environment variables +# Check if the Application Insights Instrumentation Key is set in the environment variables connection_string = os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING") if connection_string: # Configure Application Insights if the Instrumentation Key is found - configure_azure_monitor(connection_string=connection_string) + # configure_azure_monitor(connection_string=connection_string) logging.info( "Application Insights configured with the provided Instrumentation Key" ) @@ -51,7 +51,6 @@ # Configure logging logging.basicConfig(level=logging.INFO) - # Suppress INFO logs from 'azure.core.pipeline.policies.http_logging_policy' logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel( logging.WARNING diff --git a/src/backend/kernel_agents/agent_base.py b/src/backend/kernel_agents/agent_base.py index 9abab5fbe..2214751b5 100644 --- a/src/backend/kernel_agents/agent_base.py +++ b/src/backend/kernel_agents/agent_base.py @@ -276,8 +276,8 @@ async def _create_azure_ai_agent_definition( # # First try to get an existing agent with this name as assistant_id try: agent_id = None - agent_list = await client.agents.list_agents() - for agent in agent_list.data: + agent_list = client.agents.list_agents() + async for agent in agent_list: if agent.name == agent_name: agent_id = agent.id break diff --git a/src/backend/kernel_agents/agent_factory.py b/src/backend/kernel_agents/agent_factory.py index c098027ea..770dcf94f 100644 --- a/src/backend/kernel_agents/agent_factory.py +++ b/src/backend/kernel_agents/agent_factory.py @@ -7,7 +7,7 @@ # Import the new AppConfig instance from app_config import config from azure.ai.agents.models import (ResponseFormatJsonSchema, - ResponseFormatJsonSchemaType) + ResponseFormatJsonSchemaType) from context.cosmos_memory_kernel import CosmosMemoryContext from kernel_agents.agent_base import BaseAgent from kernel_agents.generic_agent import GenericAgent @@ -326,4 +326,4 @@ def clear_cache(cls, session_id: Optional[str] = None) -> None: else: cls._agent_cache.clear() cls._azure_ai_agent_cache.clear() - logger.info("Cleared all agent caches") \ No newline at end of file + logger.info("Cleared all agent caches") diff --git a/src/backend/kernel_agents/planner_agent.py b/src/backend/kernel_agents/planner_agent.py index e7b7f9117..c87516542 100644 --- a/src/backend/kernel_agents/planner_agent.py +++ b/src/backend/kernel_agents/planner_agent.py @@ -4,7 +4,7 @@ from typing import Any, Dict, List, Optional, Tuple from azure.ai.agents.models import (ResponseFormatJsonSchema, - ResponseFormatJsonSchemaType) + ResponseFormatJsonSchemaType) from context.cosmos_memory_kernel import CosmosMemoryContext from event_utils import track_event_if_configured from kernel_agents.agent_base import BaseAgent @@ -596,4 +596,4 @@ def _get_template(): Choose from {{$agents_str}} ONLY for planning your steps. """ - return instruction_template \ No newline at end of file + return instruction_template From adeea710f72096570cd8c8d68f16e51b339bbdc8 Mon Sep 17 00:00:00 2001 From: Abdul-Microsoft Date: Mon, 16 Jun 2025 18:33:46 +0530 Subject: [PATCH 15/16] Remove unused AI Foundry resource configurations and clean up related comments in main.bicep --- infra/main.bicep | 246 +---------------------------------------------- 1 file changed, 4 insertions(+), 242 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index 00f1b14a9..ebaab8004 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -147,26 +147,6 @@ param aiFoundryAiServicesConfiguration aiServicesConfigurationType = { modelCapacity: 50 } -// @description('Optional. The configuration to apply for the AI Foundry Storage Account resource.') -// param aiFoundryStorageAccountConfiguration storageAccountType = { -// enabled: true -// name: replace('sthub${solutionPrefix}', '-', '') -// location: azureOpenAILocation -// tags: tags -// sku: 'Standard_ZRS' -// subnetResourceId: null //Default value set on module configuration -// } - -// @description('Optional. The configuration to apply for the AI Foundry AI Hub resource.') -// param aiFoundryAiHubConfiguration aiHubType = { -// enabled: true -// name: 'aih-${solutionPrefix}' -// location: azureOpenAILocation -// sku: 'Basic' -// tags: tags -// subnetResourceId: null //Default value set on module configuration -// } - @description('Optional. The configuration to apply for the AI Foundry AI Project resource.') param aiFoundryAiProjectConfiguration aiProjectConfigurationType = { enabled: true @@ -802,11 +782,6 @@ module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.11.0' // principalType: 'ServicePrincipal' // roleDefinitionIdOrName: 'Cognitive Services OpenAI User' // } - // { - // principalId: containerApp.outputs.?systemAssignedMIPrincipalId! - // principalType: 'ServicePrincipal' - // roleDefinitionIdOrName: '53ca6127-db72-4b80-b1b0-d745d6d5456d' - // } // ] deployments: aiFoundryAiServicesConfiguration.?deployments ?? [ { @@ -826,169 +801,11 @@ module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.11.0' } } -// AI Foundry: storage account -// WAF best practices for Azure Blob Storage: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-blob-storage -// var storageAccountPrivateDnsZones = { -// 'privatelink.blob.${environment().suffixes.storage}': 'blob' -// 'privatelink.file.${environment().suffixes.storage}': 'file' -// } - -// module privateDnsZonesAiFoundryStorageAccount 'br/public:avm/res/network/private-dns-zone:0.3.1' = [ -// for zone in objectKeys(storageAccountPrivateDnsZones): if (virtualNetworkEnabled && aiFoundryStorageAccountEnabled) { -// name: take( -// 'avm.res.network.private-dns-zone.storage-account.${uniqueString(aiFoundryStorageAccountResourceName,zone)}.${solutionPrefix}', -// 64 -// ) -// params: { -// name: zone -// tags: tags -// enableTelemetry: enableTelemetry -// virtualNetworkLinks: [ -// { -// name: 'vnetlink-${split(zone, '.')[1]}' -// virtualNetworkResourceId: virtualNetwork.outputs.resourceId -// } -// ] -// } -// } -// ] -// var aiFoundryStorageAccountEnabled = aiFoundryStorageAccountConfiguration.?enabled ?? true -// var aiFoundryStorageAccountResourceName = aiFoundryStorageAccountConfiguration.?name ?? replace( -// 'sthub${solutionPrefix}', -// '-', -// '' -// ) - -// module aiFoundryStorageAccount 'br/public:avm/res/storage/storage-account:0.18.2' = if (aiFoundryStorageAccountEnabled) { -// name: take('avm.res.storage.storage-account.${aiFoundryStorageAccountResourceName}', 64) -// dependsOn: [ -// privateDnsZonesAiFoundryStorageAccount -// ] -// params: { -// name: aiFoundryStorageAccountResourceName -// location: aiFoundryStorageAccountConfiguration.?location ?? azureOpenAILocation -// tags: aiFoundryStorageAccountConfiguration.?tags ?? tags -// enableTelemetry: enableTelemetry -// diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] -// skuName: aiFoundryStorageAccountConfiguration.?sku ?? 'Standard_ZRS' -// allowSharedKeyAccess: false -// networkAcls: { -// bypass: 'AzureServices' -// defaultAction: 'Allow' -// } -// blobServices: { -// deleteRetentionPolicyEnabled: false -// containerDeleteRetentionPolicyDays: 7 -// containerDeleteRetentionPolicyEnabled: false -// diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] -// } -// publicNetworkAccess: virtualNetworkEnabled ? 'Disabled' : 'Enabled' -// allowBlobPublicAccess: false -// privateEndpoints: virtualNetworkEnabled -// ? map(items(storageAccountPrivateDnsZones), zone => { -// name: 'pep-${zone.value}-${aiFoundryStorageAccountResourceName}' -// customNetworkInterfaceName: 'nic-${zone.value}-${aiFoundryStorageAccountResourceName}' -// service: zone.value -// subnetResourceId: aiFoundryStorageAccountConfiguration.?subnetResourceId ?? virtualNetwork.outputs.subnetResourceIds[0] ?? '' -// privateDnsZoneResourceIds: [resourceId('Microsoft.Network/privateDnsZones', zone.key)] -// }) -// : null -// roleAssignments: [ -// { -// principalId: userAssignedIdentity.outputs.principalId -// roleDefinitionIdOrName: 'Storage Blob Data Contributor' -// } -// ] -// } -// } - -// AI Foundry: AI Hub -// WAF best practices for Open AI: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-openai -// var mlTargetSubResource = 'amlworkspace' -// var mlPrivateDnsZones = { -// 'privatelink.api.azureml.ms': mlTargetSubResource -// 'privatelink.notebooks.azure.net': mlTargetSubResource -// } -// module privateDnsZonesAiFoundryWorkspaceHub 'br/public:avm/res/network/private-dns-zone:0.3.1' = [ -// for zone in objectKeys(mlPrivateDnsZones): if (virtualNetworkEnabled && aiFoundryAiHubEnabled) { -// name: take('avm.res.network.private-dns-zone.ai-hub.${uniqueString(aiFoundryAiHubName,zone)}.${solutionPrefix}', 64) -// params: { -// name: zone -// enableTelemetry: enableTelemetry -// tags: tags -// virtualNetworkLinks: [ -// { -// name: 'vnetlink-${split(zone, '.')[1]}' -// virtualNetworkResourceId: virtualNetwork.outputs.resourceId -// } -// ] -// } -// } -// ] -// var aiFoundryAiHubEnabled = aiFoundryAiHubConfiguration.?enabled ?? true -// var aiFoundryAiHubName = aiFoundryAiHubConfiguration.?name ?? 'aih-${solutionPrefix}' -// module aiFoundryAiHub 'modules/ai-hub.bicep' = if (aiFoundryAiHubEnabled) { -// name: take('module.ai-hub.${aiFoundryAiHubName}', 64) -// dependsOn: [ -// privateDnsZonesAiFoundryWorkspaceHub -// ] -// params: { -// name: aiFoundryAiHubName -// location: aiFoundryAiHubConfiguration.?location ?? azureOpenAILocation -// tags: aiFoundryAiHubConfiguration.?tags ?? tags -// sku: aiFoundryAiHubConfiguration.?sku ?? 'Basic' -// aiFoundryAiServicesName: aiFoundryAiServices.outputs.name -// applicationInsightsResourceId: applicationInsights.outputs.resourceId -// enableTelemetry: enableTelemetry -// logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceId -// storageAccountResourceId: aiFoundryStorageAccount.outputs.resourceId -// virtualNetworkEnabled: virtualNetworkEnabled -// privateEndpoints: virtualNetworkEnabled -// ? [ -// { -// name: 'pep-${aiFoundryAiHubName}' -// customNetworkInterfaceName: 'nic-${aiFoundryAiHubName}' -// service: mlTargetSubResource -// subnetResourceId: virtualNetworkEnabled -// ? aiFoundryAiHubConfiguration.?subnetResourceId ?? virtualNetwork.?outputs.?subnetResourceIds[0] -// : null -// privateDnsZoneGroup: { -// privateDnsZoneGroupConfigs: map(objectKeys(mlPrivateDnsZones), zone => { -// name: replace(zone, '.', '-') -// privateDnsZoneResourceId: resourceId('Microsoft.Network/privateDnsZones', zone) -// }) -// } -// } -// ] -// : [] -// } -// } - // AI Foundry: AI Project // WAF best practices for Open AI: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-openai // var aiFoundryAiProjectEnabled = aiFoundryAiProjectConfiguration.?enabled ?? true var aiFoundryAiProjectName = aiFoundryAiProjectConfiguration.?name ?? 'aifp-${solutionPrefix}' - -// module aiFoundryAiProject 'br/public:avm/res/machine-learning-services/workspace:0.12.0' = if (aiFoundryAiProjectEnabled) { -// name: take('avm.res.machine-learning-services.workspace.${aiFoundryAiProjectName}', 64) -// params: { -// name: aiFoundryAiProjectName -// location: aiFoundryAiProjectConfiguration.?location ?? azureOpenAILocation -// tags: aiFoundryAiProjectConfiguration.?tags ?? tags -// enableTelemetry: enableTelemetry -// diagnosticSettings: [{ workspaceResourceId: logAnalyticsWorkspaceId }] -// sku: aiFoundryAiProjectConfiguration.?sku ?? 'Basic' -// kind: 'Project' -// hubResourceId: aiFoundryAiHub.outputs.resourceId -// roleAssignments: [ -// { -// principalId: containerApp.outputs.?systemAssignedMIPrincipalId! -// // Assigning the role with the role name instead of the role ID freezes the deployment at this point -// roleDefinitionIdOrName: '64702f94-c441-49e6-a78b-ef80e0188fee' //'Azure AI Developer' -// } -// ] -// } -// } +var aiProjectDescription = 'AI Foundry Project' resource aiServices 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { name: aiFoundryAiServicesResourceName @@ -997,8 +814,6 @@ resource aiServices 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' ex ] } -var aiProjectDescription = 'AI Foundry Project' - resource aiFoundryProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { parent: aiServices name: aiFoundryAiProjectName @@ -1047,15 +862,15 @@ resource aiDeveloperAccessFoundry 'Microsoft.Authorization/roleAssignments@2022- } } -resource CognitiveServiceOpenAIUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { +resource cognitiveServiceOpenAIUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { name: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' } resource cognitiveServiceOpenAIUserAccessFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid(containerApp.name, aiServices.id, CognitiveServiceOpenAIUser.id) + name: guid(containerApp.name, aiServices.id, cognitiveServiceOpenAIUser.id) scope: aiServices properties: { - roleDefinitionId: CognitiveServiceOpenAIUser.id + roleDefinitionId: cognitiveServiceOpenAIUser.id principalId: containerApp.outputs.?systemAssignedMIPrincipalId! } } @@ -1268,11 +1083,6 @@ module containerApp 'br/public:avm/res/app/container-app:0.14.2' = if (container name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' value: applicationInsights.outputs.connectionString } - { - name: 'AZURE_AI_AGENT_PROJECT_CONNECTION_STRING' - value: '${toLower(replace(azureOpenAILocation,' ',''))}.api.azureml.ms;${subscription().subscriptionId};${resourceGroup().name};${aiFoundryAiProjectName}' - //Location should be the AI Foundry AI Project location - } { name: 'AZURE_AI_SUBSCRIPTION_ID' value: subscription().subscriptionId @@ -1809,54 +1619,6 @@ type aiServicesConfigurationType = { modelCapacity: int? } -// @export() -// @description('The type for the Multi-Agent Custom Automation Engine Storage Account resource configuration.') -// type storageAccountType = { -// @description('Optional. If the Storage Account resource should be deployed or not.') -// enabled: bool? - -// @description('Optional. The name of the Storage Account resource.') -// @maxLength(60) -// name: string? - -// @description('Optional. Location for the Storage Account resource.') -// @metadata({ azd: { type: 'location' } }) -// location: string? - -// @description('Optional. The tags to set for the Storage Account resource.') -// tags: object? - -// @description('Optional. The SKU for the Storage Account resource.') -// sku: ('Standard_LRS' | 'Standard_GRS' | 'Standard_RAGRS' | 'Standard_ZRS' | 'Premium_LRS' | 'Premium_ZRS')? - -// @description('Optional. The resource Id of the subnet where the Storage Account private endpoint should be created.') -// subnetResourceId: string? -// } - -@export() -@description('The type for the Multi-Agent Custom Automation Engine AI Hub resource configuration.') -type aiHubType = { - @description('Optional. If the AI Hub resource should be deployed or not.') - enabled: bool? - - @description('Optional. The name of the AI Hub resource.') - @maxLength(90) - name: string? - - @description('Optional. Location for the AI Hub resource.') - @metadata({ azd: { type: 'location' } }) - location: string? - - @description('Optional. The tags to set for the AI Hub resource.') - tags: object? - - @description('Optional. The SKU of the AI Hub resource.') - sku: ('Basic' | 'Free' | 'Standard' | 'Premium')? - - @description('Optional. The resource Id of the subnet where the AI Hub private endpoint should be created.') - subnetResourceId: string? -} - @export() @description('The type for the Multi-Agent Custom Automation Engine AI Foundry AI Project resource configuration.') type aiProjectConfigurationType = { From 0b211d1377ec1d2ec178ba590eab767ee5dd0aae Mon Sep 17 00:00:00 2001 From: Abdul-Microsoft Date: Mon, 16 Jun 2025 19:30:56 +0530 Subject: [PATCH 16/16] removed storageaccount configuration param --- infra/main.bicepparam | 3 --- 1 file changed, 3 deletions(-) diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 60a29c75c..e0be7c709 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -18,9 +18,6 @@ param applicationInsightsConfiguration = { param virtualNetworkConfiguration = { enabled: false } -// param aiFoundryStorageAccountConfiguration = { -// sku: 'Standard_LRS' -// } param webServerFarmConfiguration = { skuCapacity: 1 skuName: 'B2'