Skip to content

Commit a989dbc

Browse files
committed
Merge with new UX updates
2 parents 5c847fd + 027181d commit a989dbc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+4224
-156
lines changed

.github/workflows/azure-dev.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
USE_APPLICATION_INSIGHTS: ${{ vars.USE_APPLICATION_INSIGHTS }}
4242
USE_SEARCH_SERVICE: ${{ vars.USE_SEARCH_SERVICE }}
4343
AZURE_AI_AGENT_NAME: ${{ vars.AZURE_AI_AGENT_NAME }}
44-
AZURE_AI_AGENT_ID: ${{ vars.AZURE_AI_AGENT_ID }}
44+
AZURE_EXISTING_AGENT_ID: ${{ vars.AZURE_EXISTING_AGENT_ID }}
4545
AZURE_AI_AGENT_DEPLOYMENT_NAME: ${{ vars.AZURE_AI_AGENT_DEPLOYMENT_NAME }}
4646
AZURE_AI_AGENT_DEPLOYMENT_SKU: ${{ vars.AZURE_AI_AGENT_DEPLOYMENT_SKU }}
4747
AZURE_AI_AGENT_DEPLOYMENT_CAPACITY: ${{ vars.AZURE_AI_AGENT_DEPLOYMENT_CAPACITY }}
@@ -77,6 +77,9 @@ jobs:
7777
- name: Deploy Application
7878
run: azd deploy --no-prompt
7979

80+
deploy-experiments:
81+
name: Deploy Experiments
82+
needs: build
8083
update-experiments:
8184
name: Update Experiments
8285
needs: build

.github/workflows/template-validation.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
USE_APPLICATION_INSIGHTS: ${{ vars.USE_APPLICATION_INSIGHTS }}
3939
USE_SEARCH_SERVICE: ${{ vars.USE_SEARCH_SERVICE }}
4040
AZURE_AI_AGENT_NAME: ${{ vars.AZURE_AI_AGENT_NAME }}
41-
AZURE_AI_AGENT_ID: ${{ vars.AZURE_AI_AGENT_ID }}
41+
AZURE_EXISTING_AGENT_ID: ${{ vars.AZURE_EXISTING_AGENT_ID }}
4242
AZURE_AI_AGENT_DEPLOYMENT_NAME: ${{ vars.AZURE_AI_AGENT_DEPLOYMENT_NAME }}
4343
AZURE_AI_AGENT_DEPLOYMENT_SKU: ${{ vars.AZURE_AI_AGENT_DEPLOYMENT_SKU }}
4444
AZURE_AI_AGENT_DEPLOYMENT_CAPACITY: ${{ vars.AZURE_AI_AGENT_DEPLOYMENT_CAPACITY }}

README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ For a more comprehensive list of best practices and security recommendations for
1010

1111
## Features
1212

13-
This solution deploys a web-based chat application with an AI agent running in Azure Container Apps. The agent leverages the Azure AI Agent service and utilizes Azure AI Search for knowledge retrieval from uploaded files, enabling it to generate responses with citations. The solution also includes built-in monitoring capabilities with tracing to ensure easier troubleshooting and optimized performance.
13+
This solution deploys a web-based chat application with an AI agent running in Azure Container Apps. Here is a screenshot:
14+
![Screenshot of chatting web application showing requests and responses between assistants and the user.](docs/webapp_screenshot.png)
15+
16+
The agent leverages the Azure AI Agent service and utilizes Azure AI Search for knowledge retrieval from uploaded files, enabling it to generate responses with citations. The solution also includes built-in monitoring capabilities with tracing to ensure easier troubleshooting and optimized performance.
1417

1518
This solution creates an Azure AI Foundry hub, project and connected resources including Azure AI Services, AI Search and more. More details about the resources can be found in the [resources](#resources) documentation. There are options to enable Retrieval-Augmented Generation (RAG) and use logging, tracing, and monitoring.
1619

@@ -72,6 +75,7 @@ When you start a deployment, most parameters will have default values. You can c
7275

7376
| **Setting** | **Description** | **Default value** |
7477
|------------|----------------| ------------|
78+
| **Existing Project Connection String** | Specify an existing project connection string to be used instead of provisioning new resources. | |
7579
| **Azure Region** | Select a region with quota which supports your selected model. | |
7680
| **Model** | Choose from the [list of models supported by Azure AI Agent Service](https://learn.microsoft.com/azure/ai-services/agents/concepts/model-region-support) for your selected region. | gpt-4o-mini |
7781
| **Model Format** | Choose from OpenAI or Microsoft, depending on your model. | OpenAI |

azure.yaml

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@
22
# TODO: do we need hooks?
33
# TODO: do we need all of the variables?
44

5-
name: azd-get-started-with-ai-agents
5+
name: azd-get-started-with-ai-agents-aprilk
66
metadata:
77
template: azd-get-started-with-ai-agents@0.0.1-beta
88

9+
services:
10+
api:
11+
project: ./src
12+
language: py
13+
host: containerapp
14+
docker:
15+
remoteBuild: true
16+
917
hooks:
1018
postprovision:
1119
windows:

docs/deploy_customization.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ Once you disable these resources, they will not be deployed when you run `azd up
2222
By default, this template will use a naming convention with unique strings to prevent naming collisions within Azure.
2323
To override default naming conventions, the following keys can be set:
2424

25+
* `AZURE_EXISTING_AIPROJECT_CONNECTION_STRING` - An existing connection string to be use. If specified, resources for AI Foundry Hub, AI Foundry Project, and Azure AI service will not be created.
2526
* `AZURE_AIHUB_NAME` - The name of the AI Foundry Hub resource
2627
* `AZURE_AIPROJECT_NAME` - The name of the AI Foundry Project
27-
* `AZURE_AIENDPOINT_NAME` - The name of the AI Foundry online endpoint used for deployments
2828
* `AZURE_AISERVICES_NAME` - The name of the Azure AI service
2929
* `AZURE_SEARCH_SERVICE_NAME` - The name of the Azure Search service
3030
* `AZURE_STORAGE_ACCOUNT_NAME` - The name of the Storage Account

docs/webapp_screenshot.png

120 KB
Loading

infra/api.bicep

+6-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ param tags object = {}
44

55
param identityName string
66
param containerAppsEnvironmentName string
7+
param containerRegistryName string
8+
param serviceName string = 'api'
79
param projectConnectionString string
810
param agentDeploymentName string
911
param searchConnectionName string
@@ -27,15 +29,15 @@ var env = [
2729
value: apiIdentity.properties.clientId
2830
}
2931
{
30-
name: 'AZURE_AIPROJECT_CONNECTION_STRING'
32+
name: 'AZURE_EXISTING_AIPROJECT_CONNECTION_STRING'
3133
value: projectConnectionString
3234
}
3335
{
3436
name: 'AZURE_AI_AGENT_NAME'
3537
value: agentName
3638
}
3739
{
38-
name: 'AZURE_AI_AGENT_ID'
40+
name: 'AZURE_EXISTING_AGENT_ID'
3941
value: agentID
4042
}
4143
{
@@ -78,9 +80,10 @@ module app 'core/host/container-app-upsert.bicep' = {
7880
params: {
7981
name: name
8082
location: location
81-
tags: tags
83+
tags: union(tags, { 'azd-service-name': serviceName })
8284
identityName: apiIdentity.name
8385
containerAppsEnvironmentName: containerAppsEnvironmentName
86+
containerRegistryName: containerRegistryName
8487
targetPort: 50505
8588
env: env
8689
projectName: projectName

infra/core/ai/hub-dependencies.bicep

+15
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ param aiServiceModelDeployments array = []
1313
param logAnalyticsName string = ''
1414
@description('Name of the Application Insights instance')
1515
param applicationInsightsName string = ''
16+
@description('Name of the container registry')
17+
param containerRegistryName string = ''
1618
@description('Name of the Azure Cognitive Search service')
1719
param searchServiceName string = ''
1820

@@ -112,6 +114,15 @@ module applicationInsights '../monitor/applicationinsights.bicep' =
112114
}
113115
}
114116

117+
module containerRegistry '../host/container-registry.bicep' =
118+
if (!empty(containerRegistryName)) {
119+
name: 'containerRegistry'
120+
params: {
121+
location: location
122+
tags: tags
123+
name: containerRegistryName
124+
}
125+
}
115126

116127
module cognitiveServices '../ai/cognitiveservices.bicep' = {
117128
name: 'cognitiveServices'
@@ -143,6 +154,10 @@ output keyVaultEndpoint string = keyVault.outputs.endpoint
143154
output storageAccountId string = storageAccount.outputs.id
144155
output storageAccountName string = storageAccount.outputs.name
145156

157+
output containerRegistryId string = !empty(containerRegistryName) ? containerRegistry.outputs.id : ''
158+
output containerRegistryName string = !empty(containerRegistryName) ? containerRegistry.outputs.name : ''
159+
output containerRegistryEndpoint string = !empty(containerRegistryName) ? containerRegistry.outputs.loginServer : ''
160+
146161
output applicationInsightsId string = !empty(applicationInsightsName) ? applicationInsights.outputs.id : ''
147162
output applicationInsightsName string = !empty(applicationInsightsName) ? applicationInsights.outputs.name : ''
148163
output logAnalyticsWorkspaceId string = !empty(logAnalyticsName) ? logAnalytics.outputs.id : ''

infra/core/host/ai-environment.bicep

+5
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ module hubDependencies '../ai/hub-dependencies.bicep' = {
3535
tags: tags
3636
keyVaultName: keyVaultName
3737
storageAccountName: storageAccountName
38+
containerRegistryName: containerRegistryName
3839
applicationInsightsName: applicationInsightsName
3940
logAnalyticsName: logAnalyticsName
4041
aiServicesName: aiServicesName
@@ -92,6 +93,10 @@ output keyVaultEndpoint string = hubDependencies.outputs.keyVaultEndpoint
9293
output applicationInsightsName string = hubDependencies.outputs.applicationInsightsName
9394
output logAnalyticsWorkspaceName string = hubDependencies.outputs.logAnalyticsWorkspaceName
9495

96+
// Container Registry
97+
output containerRegistryName string = hubDependencies.outputs.containerRegistryName
98+
output containerRegistryEndpoint string = hubDependencies.outputs.containerRegistryEndpoint
99+
95100
// Storage Account
96101
output storageAccountName string = hubDependencies.outputs.storageAccountName
97102

infra/core/host/container-app-upsert.bicep

+13
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ param containerMinReplicas int = 1
2323
@description('The name of the container')
2424
param containerName string = 'main'
2525

26+
@description('The name of the container registry')
27+
param containerRegistryName string = ''
28+
29+
@description('Hostname suffix for container registry. Set when deploying to sovereign clouds')
30+
param containerRegistryHostSuffix string = 'azurecr.io'
31+
2632
@allowed([ 'http', 'grpc' ])
2733
@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC')
2834
param daprAppProtocol string = 'http'
@@ -43,6 +49,9 @@ param identityType string = 'None'
4349
@description('The name of the user-assigned identity')
4450
param identityName string = ''
4551

52+
@description('The name of the container image')
53+
param imageName string = ''
54+
4655
@description('The secrets required for the container')
4756
@secure()
4857
param secrets object = {}
@@ -73,6 +82,8 @@ module app 'container-app.bicep' = {
7382
ingressEnabled: ingressEnabled
7483
containerName: containerName
7584
containerAppsEnvironmentName: containerAppsEnvironmentName
85+
containerRegistryName: containerRegistryName
86+
containerRegistryHostSuffix: containerRegistryHostSuffix
7687
containerCpuCoreCount: containerCpuCoreCount
7788
containerMemory: containerMemory
7889
containerMinReplicas: containerMinReplicas
@@ -83,12 +94,14 @@ module app 'container-app.bicep' = {
8394
secrets: secrets
8495
external: external
8596
env: env
97+
imageName: imageName
8698
targetPort: targetPort
8799
serviceBinds: serviceBinds
88100
dependOn: projectName
89101
}
90102
}
91103

92104
output defaultDomain string = app.outputs.defaultDomain
105+
output imageName string = app.outputs.imageName
93106
output name string = app.outputs.name
94107
output uri string = app.outputs.uri

infra/core/host/container-app.bicep

+27-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ param containerMinReplicas int = 1
2525
@description('The name of the container')
2626
param containerName string = 'main'
2727

28+
@description('The name of the container registry')
29+
param containerRegistryName string = ''
30+
31+
@description('Hostname suffix for container registry. Set when deploying to sovereign clouds')
32+
param containerRegistryHostSuffix string = 'azurecr.io'
33+
2834
@description('The protocol used by Dapr to connect to the app, e.g., http or grpc')
2935
@allowed([ 'http', 'grpc' ])
3036
param daprAppProtocol string = 'http'
@@ -48,6 +54,8 @@ param identityName string = ''
4854
@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ])
4955
param identityType string = 'None'
5056

57+
@description('The name of the container image')
58+
param imageName string = ''
5159

5260
@description('Specifies if Ingress is enabled for the container app')
5361
param ingressEnabled bool = true
@@ -76,6 +84,17 @@ resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-
7684
// Automatically set to `UserAssigned` when an `identityName` has been set
7785
var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType
7886

87+
// Private registry support requires both an ACR name and a User Assigned managed identity
88+
var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName)
89+
90+
module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) {
91+
name: '${deployment().name}-registry-access'
92+
params: {
93+
containerRegistryName: containerRegistryName
94+
principalId: usePrivateRegistry ? userIdentity.properties.principalId : ''
95+
}
96+
}
97+
7998
resource app 'Microsoft.App/containerApps@2023-05-02-preview' = {
8099
name: name
81100
location: location
@@ -84,7 +103,7 @@ resource app 'Microsoft.App/containerApps@2023-05-02-preview' = {
84103
// otherwise the container app will throw a provision error
85104
// This also forces us to use an user assigned managed identity since there would no way to
86105
// provide the system assigned identity with the ACR pull access before the app is created
87-
dependsOn: empty(dependOn)? [] : [dependOn]
106+
dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : []
88107
identity: {
89108
type: normalizedIdentityType
90109
userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null
@@ -112,7 +131,12 @@ resource app 'Microsoft.App/containerApps@2023-05-02-preview' = {
112131
value: secret.value
113132
}]
114133
service: !empty(serviceType) ? { type: serviceType } : null
115-
registries: []
134+
registries: usePrivateRegistry ? [
135+
{
136+
server: '${containerRegistryName}.${containerRegistryHostSuffix}'
137+
identity: userIdentity.id
138+
}
139+
] : []
116140
}
117141
template: {
118142
serviceBinds: !empty(serviceBinds) ? serviceBinds : null
@@ -141,6 +165,7 @@ resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01'
141165

142166
output defaultDomain string = containerAppsEnvironment.properties.defaultDomain
143167
output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId)
168+
output imageName string = imageName
144169
output name string = app.name
145170
output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {}
146171
output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : ''

infra/core/host/container-apps.bicep

+16
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ param location string = resourceGroup().location
44
param tags object = {}
55

66
param containerAppsEnvironmentName string
7+
param containerRegistryName string
8+
param containerRegistryResourceGroupName string = ''
9+
param containerRegistryAdminUserEnabled bool = false
710
param logAnalyticsWorkspaceName string
811
param applicationInsightsName string = ''
912

@@ -18,7 +21,20 @@ module containerAppsEnvironment 'container-apps-environment.bicep' = {
1821
}
1922
}
2023

24+
module containerRegistry 'container-registry.bicep' = {
25+
name: '${name}-container-registry'
26+
scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup()
27+
params: {
28+
name: containerRegistryName
29+
location: location
30+
adminUserEnabled: containerRegistryAdminUserEnabled
31+
tags: tags
32+
}
33+
}
34+
2135
output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain
2236
output environmentName string = containerAppsEnvironment.outputs.name
2337
output environmentId string = containerAppsEnvironment.outputs.id
2438

39+
output registryLoginServer string = containerRegistry.outputs.loginServer
40+
output registryName string = containerRegistry.outputs.name

0 commit comments

Comments
 (0)