Skip to content

Commit 63b4c17

Browse files
feat: Use agent operations with the Azure AI Search tool for ChatWithCallTranscripts
2 parents 52f8a9b + 0f623db commit 63b4c17

27 files changed

+1650
-1578
lines changed

.github/workflows/deploy-KMGeneric.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ jobs:
112112
echo "Generated SOLUTION_PREFIX: ${UNIQUE_SOLUTION_PREFIX}"
113113
- name: Determine Tag Name Based on Branch
114114
id: determine_tag
115-
run: echo "tagname=${{ github.ref_name == 'main' && 'latest' || github.ref_name == 'dev' && 'dev' || github.ref_name == 'demo' && 'demo' || github.ref_name == 'dependabotchanges' && 'dependabotchanges' || github.head_ref || 'default' }}" >> $GITHUB_OUTPUT
115+
run: echo "tagname=${{ github.ref_name == 'main' && 'latest_fdp' || github.ref_name == 'dev' && 'dev' || github.ref_name == 'demo' && 'demo' || github.ref_name == 'dependabotchanges' && 'dependabotchanges' || github.head_ref || 'default' }}" >> $GITHUB_OUTPUT
116116
- name: Deploy Bicep Template
117117
id: deploy
118118
run: |

infra/deploy_ai_foundry.bicep

Lines changed: 115 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ var keyvaultName = '${abbrs.security.keyVault}${solutionName}'
2424
var location = solutionLocation //'eastus2'
2525
var aiProjectName = '${abbrs.ai.aiFoundryProject}${solutionName}'
2626
var aiSearchName = '${abbrs.ai.aiSearch}${solutionName}'
27+
var aiSearchConnectionName = 'myVectorStoreProjectConnectionName-${solutionName}'
2728

2829
var aiModelDeployments = [
2930
{
@@ -56,8 +57,8 @@ var existingOpenAIEndpoint = !empty(azureExistingAIProjectResourceId) ? format('
5657
var existingProjEndpoint = !empty(azureExistingAIProjectResourceId) ? format('https://{0}.services.ai.azure.com/api/projects/{1}', split(azureExistingAIProjectResourceId, '/')[8], split(azureExistingAIProjectResourceId, '/')[10]) : ''
5758
var existingAIServicesName = !empty(azureExistingAIProjectResourceId) ? split(azureExistingAIProjectResourceId, '/')[8] : ''
5859
var existingAIProjectName = !empty(azureExistingAIProjectResourceId) ? split(azureExistingAIProjectResourceId, '/')[10] : ''
59-
var existingAIServiceSubscription = !empty(azureExistingAIProjectResourceId) ? split(azureExistingAIProjectResourceId, '/')[2] : ''
60-
var existingAIServiceResourceGroup = !empty(azureExistingAIProjectResourceId) ? split(azureExistingAIProjectResourceId, '/')[4] : ''
60+
var existingAIServiceSubscription = !empty(azureExistingAIProjectResourceId) ? split(azureExistingAIProjectResourceId, '/')[2] : subscription().subscriptionId
61+
var existingAIServiceResourceGroup = !empty(azureExistingAIProjectResourceId) ? split(azureExistingAIProjectResourceId, '/')[4] : resourceGroup().name
6162

6263
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
6364
name: keyVaultName
@@ -161,6 +162,9 @@ resource aiSearch 'Microsoft.Search/searchServices@2024-06-01-preview' = {
161162
sku: {
162163
name: 'basic'
163164
}
165+
identity: {
166+
type: 'SystemAssigned'
167+
}
164168
properties: {
165169
replicaCount: 1
166170
partitionCount: 1
@@ -172,10 +176,7 @@ resource aiSearch 'Microsoft.Search/searchServices@2024-06-01-preview' = {
172176
encryptionWithCmk: {
173177
enforcement: 'Unspecified'
174178
}
175-
disableLocalAuth: false
176-
authOptions: {
177-
apiKeyOnly: {}
178-
}
179+
disableLocalAuth: true
179180
semanticSearch: 'free'
180181
}
181182
}
@@ -192,7 +193,7 @@ resource aiProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-pre
192193
}
193194

194195
resource aiproject_aisearch_connection_new 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = if (empty(azureExistingAIProjectResourceId)) {
195-
name: 'myVectorStoreProjectConnectionName-${solutionName}'
196+
name: aiSearchConnectionName
196197
parent: aiProject
197198
properties: {
198199
category: 'CognitiveSearch'
@@ -216,31 +217,121 @@ module existing_AIProject_SearchConnectionModule 'deploy_aifp_aisearch_connectio
216217
aiSearchName: aiSearchName
217218
aiSearchResourceId: aiSearch.id
218219
aiSearchLocation: aiSearch.location
219-
solutionName: solutionName
220+
aiSearchConnectionName: aiSearchConnectionName
220221
}
221222
}
222223

223224
resource aiUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
224225
name: '53ca6127-db72-4b80-b1b0-d745d6d5456d'
225226
}
226227

227-
resource aiUserAccessFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
228-
name: guid(resourceGroup().id, managedIdentityObjectId, aiUser.id)
228+
resource assignFoundryRoleToMI 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (empty(azureExistingAIProjectResourceId)) {
229+
name: guid(resourceGroup().id, aiServices.id, aiUser.id)
230+
scope: aiServices
229231
properties: {
230232
principalId: managedIdentityObjectId
231233
roleDefinitionId: aiUser.id
232-
principalType: 'ServicePrincipal'
234+
principalType: 'ServicePrincipal'
233235
}
234236
}
235237

236-
module assignAiUserRoleToManagedIdentity 'deploy_foundry_role_assignment.bicep' = if(!empty(azureExistingAIProjectResourceId)) {
237-
name: 'assignAiUserRoleToManagedIdentity'
238+
module assignFoundryRoleToMIExisting 'deploy_foundry_role_assignment.bicep' = if (!empty(azureExistingAIProjectResourceId)) {
239+
name: 'assignFoundryRoleToMI'
238240
scope: resourceGroup(existingAIServiceSubscription, existingAIServiceResourceGroup)
239241
params: {
240242
roleDefinitionId: aiUser.id
241-
roleAssignmentName: guid(managedIdentityObjectId, aiServices.id, aiUser.id)
243+
roleAssignmentName: guid(resourceGroup().id, managedIdentityObjectId, aiUser.id, 'foundry')
244+
aiServicesName: !empty(azureExistingAIProjectResourceId) ? existingAIServicesName : aiServicesName
245+
aiProjectName: !empty(azureExistingAIProjectResourceId) ? existingAIProjectName : aiProjectName
246+
principalId: managedIdentityObjectId
247+
}
248+
}
249+
250+
resource assignAiUserToAiFoundryCU 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
251+
name: guid(resourceGroup().id, aiServices_CU.id, aiUser.id)
252+
scope: aiServices_CU
253+
properties: {
254+
principalId: managedIdentityObjectId
255+
roleDefinitionId: aiUser.id
256+
principalType: 'ServicePrincipal'
257+
}
258+
}
259+
260+
resource cognitiveServicesOpenAIUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
261+
name: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'
262+
}
263+
264+
module assignOpenAIRoleToAISearch 'deploy_foundry_role_assignment.bicep' = {
265+
name: 'assignOpenAIRoleToAISearch'
266+
scope: resourceGroup(existingAIServiceSubscription, existingAIServiceResourceGroup)
267+
params: {
268+
roleDefinitionId: cognitiveServicesOpenAIUser.id
269+
roleAssignmentName: guid(resourceGroup().id, aiSearch.id, cognitiveServicesOpenAIUser.id, 'openai-foundry')
242270
aiServicesName: !empty(azureExistingAIProjectResourceId) ? existingAIServicesName : aiServicesName
243-
userassignedIdentityId: managedIdentityObjectId
271+
aiProjectName: !empty(azureExistingAIProjectResourceId) ? existingAIProjectName : aiProjectName
272+
principalId: aiSearch.identity.principalId
273+
}
274+
}
275+
276+
resource searchIndexDataReader 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
277+
name: '1407120a-92aa-4202-b7e9-c0e197c71c8f'
278+
}
279+
280+
resource assignSearchIndexDataReaderToAiProject 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (empty(azureExistingAIProjectResourceId)) {
281+
name: guid(resourceGroup().id, aiProject.id, searchIndexDataReader.id)
282+
scope: aiSearch
283+
properties: {
284+
principalId: aiProject.identity.principalId
285+
roleDefinitionId: searchIndexDataReader.id
286+
principalType: 'ServicePrincipal'
287+
}
288+
}
289+
290+
resource assignSearchIndexDataReaderToExistingAiProject 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (!empty(azureExistingAIProjectResourceId)) {
291+
name: guid(resourceGroup().id, existingAIProjectName, searchIndexDataReader.id, 'Existing')
292+
scope: aiSearch
293+
properties: {
294+
principalId: assignOpenAIRoleToAISearch.outputs.aiProjectPrincipalId
295+
roleDefinitionId: searchIndexDataReader.id
296+
principalType: 'ServicePrincipal'
297+
}
298+
}
299+
300+
resource searchServiceContributor 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
301+
name: '7ca78c08-252a-4471-8644-bb5ff32d4ba0'
302+
}
303+
304+
resource assignSearchServiceContributorToAiProject 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (empty(azureExistingAIProjectResourceId)) {
305+
name: guid(resourceGroup().id, aiProject.id, searchServiceContributor.id)
306+
scope: aiSearch
307+
properties: {
308+
principalId: aiProject.identity.principalId
309+
roleDefinitionId: searchServiceContributor.id
310+
principalType: 'ServicePrincipal'
311+
}
312+
}
313+
314+
resource assignSearchServiceContributorToExistingAiProject 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (!empty(azureExistingAIProjectResourceId)) {
315+
name: guid(resourceGroup().id, existingAIProjectName, searchServiceContributor.id, 'Existing')
316+
scope: aiSearch
317+
properties: {
318+
principalId: assignOpenAIRoleToAISearch.outputs.aiProjectPrincipalId
319+
roleDefinitionId: searchServiceContributor.id
320+
principalType: 'ServicePrincipal'
321+
}
322+
}
323+
324+
resource searchIndexDataContributor 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
325+
name: '8ebe5a00-799e-43f5-93ac-243d3dce84a7'
326+
}
327+
328+
resource assignSearchIndexDataContributorToMI 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
329+
name: guid(resourceGroup().id, aiProject.id, searchIndexDataContributor.id)
330+
scope: aiSearch
331+
properties: {
332+
principalId: managedIdentityObjectId
333+
roleDefinitionId: searchIndexDataContributor.id
334+
principalType: 'ServicePrincipal'
244335
}
245336
}
246337

@@ -292,6 +383,14 @@ resource azureOpenAIEndpointEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-
292383
}
293384
}
294385

386+
resource azureOpenAIEmbeddingDeploymentModel 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
387+
parent: keyVault
388+
name: 'AZURE-OPENAI-EMBEDDING-MODEL'
389+
properties: {
390+
value: embeddingModel
391+
}
392+
}
393+
295394
resource azureOpenAICUEndpointEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
296395
parent: keyVault
297396
name: 'AZURE-OPENAI-CU-ENDPOINT'
@@ -308,14 +407,6 @@ resource azureOpenAICUApiVersionEntry 'Microsoft.KeyVault/vaults/secrets@2021-11
308407
}
309408
}
310409

311-
resource azureSearchAdminKeyEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
312-
parent: keyVault
313-
name: 'AZURE-SEARCH-KEY'
314-
properties: {
315-
value: aiSearch.listAdminKeys().primaryKey
316-
}
317-
}
318-
319410
resource azureSearchServiceEndpointEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
320411
parent: keyVault
321412
name: 'AZURE-SEARCH-ENDPOINT'
@@ -390,6 +481,7 @@ output aiSearchId string = aiSearch.id
390481
output aiSearchTarget string = 'https://${aiSearch.name}.search.windows.net'
391482
output aiSearchService string = aiSearch.name
392483
output aiProjectName string = !empty(existingAIProjectName) ? existingAIProjectName : aiProject.name
484+
output aiSearchConnectionName string = aiSearchConnectionName
393485

394486
output applicationInsightsId string = applicationInsights.id
395487
output logAnalyticsWorkspaceResourceName string = useExisting ? existingLogAnalyticsWorkspace.name : logAnalytics.name

infra/deploy_aifp_aisearch_connection.bicep

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ param existingAIServicesName string
33
param aiSearchName string
44
param aiSearchResourceId string
55
param aiSearchLocation string
6-
param solutionName string
6+
param aiSearchConnectionName string
77

88
resource projectAISearchConnection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = {
9-
name: '${existingAIServicesName}/${existingAIProjectName}/myVectorStoreProjectConnectionName-${solutionName}'
9+
name: '${existingAIServicesName}/${existingAIProjectName}/${aiSearchConnectionName}'
1010
properties: {
1111
category: 'CognitiveSearch'
1212
target: 'https://${aiSearchName}.search.windows.net'

infra/deploy_backend_docker.bicep

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ param keyVaultName string
1313
param aiServicesName string
1414
param useLocalBuild string
1515
param azureExistingAIProjectResourceId string = ''
16+
param aiSearchName string
1617
var existingAIServiceSubscription = !empty(azureExistingAIProjectResourceId) ? split(azureExistingAIProjectResourceId, '/')[2] : subscription().subscriptionId
1718
var existingAIServiceResourceGroup = !empty(azureExistingAIProjectResourceId) ? split(azureExistingAIProjectResourceId, '/')[4] : resourceGroup().name
1819
var existingAIServicesName = !empty(azureExistingAIProjectResourceId) ? split(azureExistingAIProjectResourceId, '/')[8] : ''
@@ -143,6 +144,23 @@ resource keyVaultSecretsUserAssignment 'Microsoft.Authorization/roleAssignments@
143144
}
144145
}
145146

147+
resource aiSearch 'Microsoft.Search/searchServices@2024-06-01-preview' existing = {
148+
name: aiSearchName
149+
}
150+
151+
resource searchIndexDataReader 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
152+
name: '1407120a-92aa-4202-b7e9-c0e197c71c8f'
153+
}
154+
155+
resource searchIndexDataReaderAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
156+
name: guid(appService.name, aiSearch.name, searchIndexDataReader.id)
157+
scope: aiSearch
158+
properties: {
159+
roleDefinitionId: searchIndexDataReader.id
160+
principalId: appService.outputs.identityPrincipalId
161+
}
162+
}
163+
146164
resource aiUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
147165
name: '53ca6127-db72-4b80-b1b0-d745d6d5456d'
148166
}

infra/deploy_foundry_role_assignment.bicep

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,25 @@ param principalId string = ''
22
param roleDefinitionId string
33
param roleAssignmentName string = ''
44
param aiServicesName string
5-
param userassignedIdentityId string = ''
5+
param aiProjectName string = ''
66

77
resource aiServices 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = {
88
name: aiServicesName
99
}
1010

11-
resource roleAssignmentToFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (!empty(aiServicesName) && !empty(principalId)) {
12-
name: roleAssignmentName
13-
scope: aiServices
14-
properties: {
15-
roleDefinitionId: roleDefinitionId
16-
principalId: principalId
17-
}
11+
resource aiProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' existing = if (!empty(aiProjectName)) {
12+
name: aiProjectName
13+
parent: aiServices
1814
}
1915

20-
resource roleAssignmentToManagedIdentity 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (!empty(userassignedIdentityId)) {
16+
resource roleAssignmentToFoundry 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
2117
name: roleAssignmentName
2218
scope: aiServices
2319
properties: {
2420
roleDefinitionId: roleDefinitionId
25-
principalId: userassignedIdentityId
26-
principalType: 'ServicePrincipal'
21+
principalId: principalId
2722
}
2823
}
24+
25+
output aiServicesPrincipalId string = aiServices.identity.principalId
26+
output aiProjectPrincipalId string = !empty(aiProjectName) ? aiProject.identity.principalId : ''

infra/main.bicep

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ param embeddingModel string = 'text-embedding-ada-002'
6060
@description('Capacity of the Embedding Model deployment')
6161
param embeddingDeploymentCapacity int = 80
6262

63-
param imageTag string = 'latest'
63+
param imageTag string = 'latest_fdp'
6464

6565
param AZURE_LOCATION string=''
6666
var solutionLocation = empty(AZURE_LOCATION) ? resourceGroup().location : AZURE_LOCATION
@@ -226,7 +226,8 @@ module backend_docker 'deploy_backend_docker.bicep' = {
226226
keyVaultName: kvault.outputs.keyvaultName
227227
aiServicesName: aifoundry.outputs.aiServicesName
228228
useLocalBuild: useLocalBuildLower
229-
azureExistingAIProjectResourceId: azureExistingAIProjectResourceId
229+
azureExistingAIProjectResourceId: azureExistingAIProjectResourceId
230+
aiSearchName: aifoundry.outputs.aiSearchName
230231
appSettings: {
231232
AZURE_OPENAI_DEPLOYMENT_MODEL: gptModelName
232233
AZURE_OPENAI_ENDPOINT: aifoundry.outputs.aiServicesTarget
@@ -245,9 +246,9 @@ module backend_docker 'deploy_backend_docker.bicep' = {
245246
SQLDB_USER_MID: managedIdentityModule.outputs.managedIdentityBackendAppOutput.clientId
246247

247248
AZURE_AI_SEARCH_ENDPOINT: aifoundry.outputs.aiSearchTarget
248-
AZURE_AI_SEARCH_API_KEY: '@Microsoft.KeyVault(SecretUri=${kvault.outputs.keyvaultUri}secrets/AZURE-SEARCH-KEY/)'
249249
AZURE_AI_SEARCH_INDEX: 'call_transcripts_index'
250-
USE_AI_PROJECT_CLIENT: 'False'
250+
AZURE_AI_SEARCH_CONNECTION_NAME: aifoundry.outputs.aiSearchConnectionName
251+
USE_AI_PROJECT_CLIENT: 'True'
251252
DISPLAY_CHART_DEFAULT: 'False'
252253
APPLICATIONINSIGHTS_CONNECTION_STRING: aifoundry.outputs.applicationInsightsConnectionString
253254
DUMMY_TEST: 'True'
@@ -285,6 +286,7 @@ output AZURE_AI_PROJECT_NAME string = aifoundry.outputs.aiProjectName
285286
output AZURE_AI_SEARCH_API_KEY string = ''
286287
output AZURE_AI_SEARCH_ENDPOINT string = aifoundry.outputs.aiSearchTarget
287288
output AZURE_AI_SEARCH_INDEX string = 'call_transcripts_index'
289+
output AZURE_AI_SEARCH_CONNECTION_NAME string = aifoundry.outputs.aiSearchConnectionName
288290
output AZURE_COSMOSDB_ACCOUNT string = cosmosDBModule.outputs.cosmosAccountName
289291
output AZURE_COSMOSDB_CONVERSATIONS_CONTAINER string = 'conversations'
290292
output AZURE_COSMOSDB_DATABASE string = 'db_conversation_history'

0 commit comments

Comments
 (0)