-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
121 additions
and
361 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,296 +1,65 @@ | ||
@description('List of Mock webapp names used to simulate OpenAI behavior.') | ||
param mockWebApps array = [] | ||
// ------------------ | ||
// PARAMETERS | ||
// ------------------ | ||
|
||
@description('The name of the OpenAI mock backend pool') | ||
param mockBackendPoolName string = 'openai-backend-pool' | ||
// Typically, parameters would be decorated with appropriate metadata and attributes, but as they are very repetetive in these labs we omit them for brevity. | ||
|
||
@description('The description of the OpenAI mock backend pool') | ||
param mockBackendPoolDescription string = 'Load balancer for multiple OpenAI Mocking endpoints' | ||
|
||
@description('List of OpenAI resources to create. Add pairs of name and location.') | ||
param apimSku string | ||
param openAIConfig array = [] | ||
|
||
@description('Deployment Name') | ||
param openAIDeploymentName string | ||
|
||
@description('Azure OpenAI Sku') | ||
@allowed([ | ||
'S0' | ||
]) | ||
param openAISku string = 'S0' | ||
|
||
@description('Model Name') | ||
param openAIModelName string | ||
|
||
@description('Model Version') | ||
param openAIModelVersion string | ||
|
||
@description('Model Capacity') | ||
param openAIModelCapacity int = 20 | ||
|
||
@description('The name of the API Management resource') | ||
param apimResourceName string | ||
|
||
@description('Location for the APIM resource') | ||
param apimResourceLocation string = resourceGroup().location | ||
|
||
@description('The pricing tier of this API Management service') | ||
@allowed([ | ||
'Consumption' | ||
'Developer' | ||
'Basic' | ||
'Basicv2' | ||
'Standard' | ||
'Standardv2' | ||
'Premium' | ||
]) | ||
param apimSku string = 'Consumption' | ||
|
||
@description('The instance size of this API Management service.') | ||
@allowed([ | ||
0 | ||
1 | ||
2 | ||
]) | ||
param apimSkuCount int = 1 | ||
|
||
@description('The email address of the owner of the service') | ||
param apimPublisherEmail string = '[email protected]' | ||
|
||
@description('The name of the owner of the service') | ||
param apimPublisherName string = 'Microsoft' | ||
|
||
@description('The name of the APIM API for OpenAI API') | ||
param openAIAPIName string = 'openai' | ||
|
||
@description('The relative path of the APIM API for OpenAI API') | ||
param openAIAPIPath string = 'openai' | ||
|
||
@description('The display name of the APIM API for OpenAI API') | ||
param openAIAPIDisplayName string = 'OpenAI' | ||
|
||
@description('The description of the APIM API for OpenAI API') | ||
param openAIAPIDescription string = 'Azure OpenAI API inferencing API' | ||
|
||
@description('Full URL for the OpenAI API spec') | ||
param openAIAPISpecURL string = 'https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cognitiveservices/data-plane/AzureOpenAI/inference/stable/2024-02-01/inference.json' | ||
|
||
@description('The name of the APIM Subscription for OpenAI API') | ||
param openAISubscriptionName string = 'openai-subscription' | ||
|
||
@description('The description of the APIM Subscription for OpenAI API') | ||
param openAISubscriptionDescription string = 'OpenAI Subscription' | ||
|
||
@description('The name of the OpenAI backend pool') | ||
param openAIBackendPoolName string = 'openai-backend-pool' | ||
|
||
@description('The description of the OpenAI backend pool') | ||
param openAIBackendPoolDescription string = 'Load balancer for multiple OpenAI endpoints' | ||
|
||
var resourceSuffix = uniqueString(subscription().id, resourceGroup().id) | ||
|
||
resource cognitiveServices 'Microsoft.CognitiveServices/accounts@2021-10-01' = [for config in openAIConfig: if(length(openAIConfig) > 0) { | ||
name: '${config.name}-${resourceSuffix}' | ||
location: config.location | ||
sku: { | ||
name: openAISku | ||
} | ||
kind: 'OpenAI' | ||
properties: { | ||
apiProperties: { | ||
statisticsEnabled: false | ||
} | ||
customSubDomainName: toLower('${config.name}-${resourceSuffix}') | ||
} | ||
}] | ||
|
||
resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for (config, i) in openAIConfig: if(length(openAIConfig) > 0) { | ||
name: openAIDeploymentName | ||
parent: cognitiveServices[i] | ||
properties: { | ||
model: { | ||
format: 'OpenAI' | ||
name: openAIModelName | ||
version: openAIModelVersion | ||
} | ||
} | ||
sku: { | ||
name: 'Standard' | ||
capacity: openAIModelCapacity | ||
} | ||
}] | ||
|
||
resource apimService 'Microsoft.ApiManagement/service@2023-05-01-preview' = { | ||
name: '${apimResourceName}-${resourceSuffix}' | ||
location: apimResourceLocation | ||
sku: { | ||
name: apimSku | ||
capacity: (apimSku == 'Consumption') ? 0 : ((apimSku == 'Developer') ? 1 : apimSkuCount) | ||
} | ||
properties: { | ||
publisherEmail: apimPublisherEmail | ||
publisherName: apimPublisherName | ||
} | ||
identity: { | ||
type: 'SystemAssigned' | ||
} | ||
} | ||
|
||
var roleDefinitionID = resourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd') | ||
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (config, i) in openAIConfig: if(length(openAIConfig) > 0) { | ||
scope: cognitiveServices[i] | ||
name: guid(subscription().id, resourceGroup().id, config.name, roleDefinitionID) | ||
properties: { | ||
roleDefinitionId: roleDefinitionID | ||
principalId: apimService.identity.principalId | ||
principalType: 'ServicePrincipal' | ||
} | ||
}] | ||
|
||
resource api 'Microsoft.ApiManagement/service/apis@2023-05-01-preview' = { | ||
name: openAIAPIName | ||
parent: apimService | ||
properties: { | ||
apiType: 'http' | ||
description: openAIAPIDescription | ||
displayName: openAIAPIDisplayName | ||
format: 'openapi-link' | ||
path: openAIAPIPath | ||
protocols: [ | ||
'https' | ||
] | ||
subscriptionKeyParameterNames: { | ||
header: 'api-key' | ||
query: 'api-key' | ||
} | ||
subscriptionRequired: true | ||
type: 'http' | ||
value: openAIAPISpecURL | ||
} | ||
} | ||
|
||
resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2021-12-01-preview' = { | ||
name: 'policy' | ||
parent: api | ||
properties: { | ||
format: 'rawxml' | ||
value: loadTextContent('policy.xml') | ||
} | ||
dependsOn: [ | ||
backendOpenAI | ||
] | ||
} | ||
|
||
resource backendOpenAI 'Microsoft.ApiManagement/service/backends@2023-05-01-preview' = [for (config, i) in openAIConfig: if(length(openAIConfig) > 0) { | ||
name: config.name | ||
parent: apimService | ||
properties: { | ||
description: 'backend description' | ||
url: '${cognitiveServices[i].properties.endpoint}/openai' | ||
protocol: 'http' | ||
circuitBreaker: { | ||
rules: [ | ||
{ | ||
failureCondition: { | ||
count: 3 | ||
errorReasons: [ | ||
'Server errors' | ||
] | ||
interval: 'PT5M' | ||
statusCodeRanges: [ | ||
{ | ||
min: 429 | ||
max: 429 | ||
} | ||
] | ||
} | ||
name: 'openAIBreakerRule' | ||
tripDuration: 'PT1M' | ||
} | ||
] | ||
} | ||
} | ||
}] | ||
|
||
resource backendMock 'Microsoft.ApiManagement/service/backends@2023-05-01-preview' = [for (mock, i) in mockWebApps: if(length(openAIConfig) == 0 && length(mockWebApps) > 0) { | ||
name: mock.name | ||
parent: apimService | ||
properties: { | ||
description: 'backend description' | ||
url: '${mock.endpoint}/openai' | ||
protocol: 'http' | ||
circuitBreaker: { | ||
rules: [ | ||
{ | ||
failureCondition: { | ||
count: 3 | ||
errorReasons: [ | ||
'Server errors' | ||
] | ||
interval: 'PT5M' | ||
statusCodeRanges: [ | ||
{ | ||
min: 429 | ||
max: 429 | ||
} | ||
] | ||
} | ||
name: 'mockBreakerRule' | ||
tripDuration: 'PT1M' | ||
} | ||
] | ||
} | ||
} | ||
}] | ||
|
||
resource backendPoolOpenAI 'Microsoft.ApiManagement/service/backends@2023-05-01-preview' = if(length(openAIConfig) > 1) { | ||
name: openAIBackendPoolName | ||
parent: apimService | ||
properties: { | ||
description: openAIBackendPoolDescription | ||
type: 'Pool' | ||
// protocol: 'http' // the protocol is not needed in the Pool type | ||
// url: '${cognitiveServices[0].properties.endpoint}/openai' // the url is not needed in the Pool type | ||
pool: { | ||
services: [for (config, i) in openAIConfig: { | ||
id: '/backends/${backendOpenAI[i].name}' | ||
} | ||
] | ||
} | ||
param openAIDeploymentName string | ||
param openAIModelSKU string | ||
param openAIModelCapacity int | ||
param openAIAPIVersion string | ||
|
||
// ------------------ | ||
// VARIABLES | ||
// ------------------ | ||
|
||
// Account for all placeholders in the polixy.xml file. | ||
var policyXml = loadTextContent('policy.xml') | ||
var updatedPolicyXml = replace(policyXml, '{backend-id}', (length(openAIConfig) > 1) ? 'openai-backend-pool' : openAIConfig[0].name) | ||
|
||
// ------------------ | ||
// RESOURCES | ||
// ------------------ | ||
|
||
// 1. API Management | ||
module apimModule '../../modules/apim/v1/apim.bicep' = { | ||
name: 'apimModule' | ||
params: { | ||
apimSku: apimSku | ||
} | ||
} | ||
|
||
resource backendPoolMock 'Microsoft.ApiManagement/service/backends@2023-05-01-preview' = if(length(openAIConfig) == 0 && length(mockWebApps) > 1) { | ||
name: mockBackendPoolName | ||
parent: apimService | ||
properties: { | ||
description: mockBackendPoolDescription | ||
type: 'Pool' | ||
// protocol: 'http' // the protocol is not needed in the Pool type | ||
// url: '${mockWebApps[0].endpoint}/openai' // the url is not needed in the Pool type | ||
pool: { | ||
services: [for (webApp, i) in mockWebApps: { | ||
id: '/backends/${backendMock[i].name}' | ||
} | ||
] | ||
} | ||
// 2. Cognitive Services | ||
module openAIModule '../../modules/cognitive-services/v1/openai.bicep' = { | ||
name: 'openAIModule' | ||
params: { | ||
openAIConfig: openAIConfig | ||
openAIDeploymentName: openAIDeploymentName | ||
openAIModelName: openAIModelName | ||
openAIModelVersion: openAIModelVersion | ||
openAIModelSKU: openAIModelSKU | ||
openAIModelCapacity: openAIModelCapacity | ||
apimPrincipalId: apimModule.outputs.principalId | ||
} | ||
} | ||
|
||
resource apimSubscription 'Microsoft.ApiManagement/service/subscriptions@2023-05-01-preview' = { | ||
name: openAISubscriptionName | ||
parent: apimService | ||
properties: { | ||
allowTracing: true | ||
displayName: openAISubscriptionDescription | ||
scope: '/apis/${api.id}' | ||
state: 'active' | ||
// 3. APIM OpenAI API | ||
module openAIAPIModule '../../modules/apim/v1/openai-api.bicep' = { | ||
name: 'openAIAPIModule' | ||
params: { | ||
policyXml: updatedPolicyXml | ||
openAIConfig: openAIModule.outputs.extendedOpenAIConfig | ||
openAIAPIVersion: openAIAPIVersion | ||
} | ||
} | ||
|
||
output apimServiceId string = apimService.id | ||
|
||
output apimResourceGatewayURL string = apimService.properties.gatewayUrl | ||
|
||
#disable-next-line outputs-should-not-contain-secrets | ||
output apimSubscriptionKey string = apimSubscription.listSecrets().primaryKey | ||
// ------------------ | ||
// MARK: OUTPUTS | ||
// ------------------ | ||
output apimServiceId string = apimModule.outputs.id | ||
output apimResourceGatewayURL string = apimModule.outputs.gatewayUrl | ||
output apimSubscriptionKey string = openAIAPIModule.outputs.subscriptionPrimaryKey |
Oops, something went wrong.