Skip to content

Commit

Permalink
Deployment with automatic scripts
Browse files Browse the repository at this point in the history
Using managed identity now
denniszielke committed Apr 26, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 81925b2 commit e6d12da
Showing 16 changed files with 388 additions and 47 deletions.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# adventure-day-agent
This repository host the adventure day agent

Regions that this deployment can be executed:
- uksouth
- swedencentral
- canadaeast
- australiaeast

## Quickstart

```
@@ -32,7 +38,6 @@ AZURE_OPENAI_EMBEDDING_VERSION = "2024-02-01"
```


## Test API for Challenge 1

Go to directory src-agents/challenge1
@@ -55,3 +60,11 @@ curl -X 'POST' \
}'
```

## Deploy resources for Challenge X

Run the following script

```
bash ./azd-hooks/deploy.sh challenge1 $ENVIRONMENT_NAME
```
67 changes: 67 additions & 0 deletions azd-hooks/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/bin/bash

set -e

CHALLENGE="$1"

if [ "$CHALLENGE" == "" ]; then
echo "No challenge name provided - aborting"
exit 0;
fi

AZURE_ENV_NAME="$2"

if [ "$AZURE_ENV_NAME" == "" ]; then
echo "No environment name provided - aborting"
exit 0;
fi

if [[ $CHALLENGE =~ ^[a-z0-9]{5,12}$ ]]; then
echo "challenge name $CHALLENGE is valid"
else
echo "challenge name $CHALLENGE is invalid - only numbers and lower case min 5 and max 12 characters allowed - aborting"
exit 0;
fi

RESOURCE_GROUP="rg-$AZURE_ENV_NAME"

if [ $(az group exists --name $RESOURCE_GROUP) = false ]; then
echo "resource group $RESOURCE_GROUP does not exist"
error=1
else
echo "resource group $RESOURCE_GROUP already exists"
LOCATION=$(az group show -n $RESOURCE_GROUP --query location -o tsv)
fi

APPINSIGHTS_NAME=$(az resource list -g $RESOURCE_GROUP --resource-type "Microsoft.Insights/components" --query "[0].name" -o tsv)
AZURE_CONTAINER_REGISTRY_NAME=$(az resource list -g $RESOURCE_GROUP --resource-type "Microsoft.ContainerRegistry/registries" --query "[0].name" -o tsv)
OPENAI_NAME=$(az resource list -g $RESOURCE_GROUP --resource-type "Microsoft.CognitiveServices/accounts" --query "[0].name" -o tsv)
ENVIRONMENT_NAME=$(az resource list -g $RESOURCE_GROUP --resource-type "Microsoft.App/managedEnvironments" --query "[0].name" -o tsv)
IDENTITY_NAME=$(az resource list -g $RESOURCE_GROUP --resource-type "Microsoft.ManagedIdentity/userAssignedIdentities" --query "[0].name" -o tsv)
SERVICE_NAME=$CHALLENGE
AZURE_SUBSCRIPTION_ID=$(az account show --query id -o tsv)

echo "container registry name: $AZURE_CONTAINER_REGISTRY_NAME"
echo "application insights name: $APPINSIGHTS_NAME"
echo "openai name: $OPENAI_NAME"
echo "environment name: $ENVIRONMENT_NAME"
echo "identity name: $IDENTITY_NAME"
echo "service name: $SERVICE_NAME"

CONTAINER_APP_EXISTS=$(az resource list -g $RESOURCE_GROUP --resource-type "Microsoft.App/containerApps" --query "[?contains(name, '$SERVICE_NAME')].id" -o tsv)
EXISTS="false"

if [ "$CONTAINER_APP_EXISTS" == "" ]; then
echo "container app $SERVICE_NAME does not exist"
else
echo "container app $SERVICE_NAME already exists"
EXISTS="true"
fi

az acr build --subscription ${AZURE_SUBSCRIPTION_ID} --registry ${AZURE_CONTAINER_REGISTRY_NAME} --image $SERVICE_NAME:latest ./src-agents/$SERVICE_NAME
IMAGE_NAME="${AZURE_CONTAINER_REGISTRY_NAME}.azurecr.io/$SERVICE_NAME:latest"

az deployment group create -g $RESOURCE_GROUP -f ./infra/app/challengeX.bicep \
-p name=$SERVICE_NAME -p location=$LOCATION -p containerAppsEnvironmentName=$ENVIRONMENT_NAME \
-p containerRegistryName=$AZURE_CONTAINER_REGISTRY_NAME -p applicationInsightsName=$APPINSIGHTS_NAME \
-p openaiName=$OPENAI_NAME -p identityName=$IDENTITY_NAME -p imageName=$IMAGE_NAME -p exists=$EXISTS
4 changes: 4 additions & 0 deletions azd-hooks/post-provision.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash

echo "Deployed environment $AZURE_ENV_NAME successfully."
echo $AZURE_ENV_NAME
30 changes: 21 additions & 9 deletions azure.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
name: adventure-day-agents
metadata:
template: openai-plugin-fastapi@0.0.1-beta
services:
challenge1:
project: ./src-agents/challenge1
dist: build
language: python
host: containerapp
docker:
path: ./Dockerfile
context: ./
hooks:
postprovision:
posix:
shell: sh
continueOnError: false
interactive: true
run: azd-hooks/post-provision.sh
# services:
# challenge1:
# project: ./src-agents/challenge1
# language: py
# host: containerapp
# module: challenge1
# challenge2:
# project: ./src-agents/challenge2
# dist: build
# language: python
# host: containerapp
# docker:
# path: ./Dockerfile
# context: ./
infra:
provider: bicep
47 changes: 47 additions & 0 deletions infra/ai/openai.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
param name string
param location string = resourceGroup().location
param tags object = {}

param kind string = 'OpenAI'
// Public network access of the Azure OpenAI service
param publicNetworkAccess string = 'Enabled'
// SKU of the Azure OpenAI service
param sku object = {
name: 'S0'
}

param customDomainName string

param deployments array

resource account 'Microsoft.CognitiveServices/accounts@2022-10-01' = {
name: name
location: location
tags: tags
kind: kind
identity: {
type: 'SystemAssigned'
}
properties: {
customSubDomainName: customDomainName
publicNetworkAccess: publicNetworkAccess
}
sku: sku
}

// Deployments for the Azure OpenAI service
@batchSize(1)
resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: {
parent: account
name: deployment.name
sku: {
name: 'Standard'
capacity: 300
}
properties: {
model: deployment.model
}
}]

output openaiEndpoint string = account.properties.endpoint
output openaiKey string = listKeys(account.id, '2022-10-01').key1
12 changes: 11 additions & 1 deletion infra/app/challenge1.bicep
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ param applicationInsightsName string
param containerAppsEnvironmentName string
param containerRegistryName string
param serviceName string = 'challenge1'
param imageName string

resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: identityName
@@ -31,6 +32,7 @@ module app '../core/host/container-app-upsert.bicep' = {
params: {
name: name
location: location
imageName: imageName
tags: union(tags, { 'azd-service-name': serviceName })
identityName: identityName
exists: exists
@@ -57,8 +59,16 @@ module app '../core/host/container-app-upsert.bicep' = {
name: 'AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME'
value: completionDeploymentName
}
{
name: 'AZURE_OPENAI_VERSION'
value: '2024-02-01'
}
{
name: 'OPENAI_API_TYPE'
value: 'azure'
}
]
targetPort: 8080
targetPort: 80
}
}

77 changes: 77 additions & 0 deletions infra/app/challengeX.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
param name string
param location string = resourceGroup().location
param exists bool = true
param containerAppsEnvironmentName string
param containerRegistryName string
param applicationInsightsName string
param identityName string
param openaiName string
param imageName string

var tags = { 'azd-env-name': containerAppsEnvironmentName }
var completionDeploymentModelName = 'gpt-35-turbo'

resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = {
name: identityName
}

resource account 'Microsoft.CognitiveServices/accounts@2022-10-01' existing = {
name: openaiName
}

var openaiEndpoint = account.properties.endpoint
var openaiApiKey = listKeys(account.id, '2022-10-01').key1

module app '../core/host/container-app-upsert.bicep' = {
name: '${name}-container-app'
params: {
name: name
location: location
tags: union(tags, { 'azd-service-name': name })
identityName: identityName
imageName: imageName
exists: exists
containerAppsEnvironmentName: containerAppsEnvironmentName
containerRegistryName: containerRegistryName
env: [
{
name: 'AZURE_CLIENT_ID'
value: apiIdentity.properties.clientId
}
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: applicationInsights.properties.ConnectionString
}
{
name: 'AZURE_OPENAI_API_KEY'
value: openaiApiKey
}
{
name: 'AZURE_OPENAI_ENDPOINT'
value: openaiEndpoint
}
{
name: 'AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME'
value: completionDeploymentModelName
}
{
name: 'AZURE_OPENAI_VERSION'
value: '2024-02-01'
}
{
name: 'OPENAI_API_TYPE'
value: 'azure'
}
]
targetPort: 8080
}
}

resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
name: applicationInsightsName
}

output SERVICE_API_IDENTITY_PRINCIPAL_ID string = apiIdentity.properties.principalId
output SERVICE_API_NAME string = app.outputs.name
output SERVICE_API_URI string = app.outputs.uri
output SERVICE_API_IMAGE_NAME string = app.outputs.imageName
3 changes: 2 additions & 1 deletion infra/core/host/container-app-upsert.bicep
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ param tags object = {}

param containerAppsEnvironmentName string
param containerName string = 'main'
param imageName string
param containerRegistryName string
param secrets array = []
param env array = []
@@ -39,7 +40,7 @@ module app 'container-app.bicep' = {
secrets: secrets
external: external
env: env
imageName: exists ? existingApp.properties.template.containers[0].image : ''
imageName: imageName
targetPort: targetPort
}
}
6 changes: 3 additions & 3 deletions infra/core/host/container-app.bicep
Original file line number Diff line number Diff line change
@@ -9,16 +9,16 @@ param secrets array = []
param env array = []
param external bool = true
param imageName string
param targetPort int = 8080
param targetPort int = 80

@description('User assigned identity name')
param identityName string = ''

@description('CPU cores allocated to a single container instance, e.g. 0.5')
param containerCpuCoreCount string = '0.5'
param containerCpuCoreCount string = '1'

@description('Memory allocated to a single container instance, e.g. 1Gi')
param containerMemory string = '1.0Gi'
param containerMemory string = '2.0Gi'

resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = {
name: identityName
71 changes: 50 additions & 21 deletions infra/main.bicep
Original file line number Diff line number Diff line change
@@ -9,32 +9,46 @@ param environmentName string
@description('Primary location for all resources')
param location string

@minLength(1)
@description('Openai API key for the API to use.')
param openaiApiKey string

@minLength(1)
@description('Openai API Endpoint for the API to use.')
param openaiEndpoint string

@minLength(1)
@description('Name of the OpenAI Completion model deployment name.')
param completionDeploymentName string

param resourceGroupName string = ''
param containerAppsEnvironmentName string = ''
param containerRegistryName string = ''
param apiContainerAppName string = ''
param openaiName string = ''
param apiContainerAppName string = 'challenge1'
param applicationInsightsDashboardName string = ''
param applicationInsightsName string = ''
param logAnalyticsName string = ''

param apiAppExists bool = false
param challenge1Exists bool = false

var abbrs = loadJsonContent('./abbreviations.json')
var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))
var tags = { 'azd-env-name': environmentName }

param completionDeploymentModelName string = 'gpt-35-turbo'
param completionModelName string = 'gpt-35-turbo'
param completionModelVersion string = '0613'
param embeddingDeploymentModelName string = 'text-embedding-ada-002'
param embeddingModelName string = 'text-embedding-ada-002'

param modelDeployments array = [
{
name: completionDeploymentModelName
model: {
format: 'OpenAI'
name: completionModelName
version: completionModelVersion
}
}
{
name: embeddingDeploymentModelName
model: {
format: 'OpenAI'
name: embeddingModelName
version: '2'
}
}
]

// Organize resources in a resource group
resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
name: !empty(resourceGroupName) ? resourceGroupName : '${abbrs.resourcesResourceGroups}${environmentName}'
@@ -55,22 +69,36 @@ module containerApps './core/host/container-apps.bicep' = {
}
}

// API
// Challenge 1 Container App
module challenge1 './app/challenge1.bicep' = {
name: 'challenge1'
scope: resourceGroup
params: {
name: !empty(apiContainerAppName) ? apiContainerAppName : '${abbrs.appContainerApps}api-${resourceToken}'
location: location
tags: tags
identityName: '${abbrs.managedIdentityUserAssignedIdentities}api-${resourceToken}'
imageName: ''
identityName: '${abbrs.managedIdentityUserAssignedIdentities}api-agents'
applicationInsightsName: monitoring.outputs.applicationInsightsName
containerAppsEnvironmentName: containerApps.outputs.environmentName
containerRegistryName: containerApps.outputs.registryName
openaiApiKey: openaiApiKey
openaiEndpoint: openaiEndpoint
completionDeploymentName: completionDeploymentName
exists: apiAppExists
openaiApiKey: openai.outputs.openaiKey
openaiEndpoint: openai.outputs.openaiEndpoint
completionDeploymentName: completionDeploymentModelName
exists: challenge1Exists
}
}

// Monitor application with Azure Monitor
module openai './ai/openai.bicep' = {
name: 'openai'
scope: resourceGroup
params: {
location: location
tags: tags
customDomainName: !empty(openaiName) ? openaiName : '${abbrs.cognitiveServicesAccounts}${resourceToken}'
name: !empty(openaiName) ? openaiName : '${abbrs.cognitiveServicesAccounts}${resourceToken}'
deployments: modelDeployments
}
}

@@ -96,4 +124,5 @@ output APPLICATIONINSIGHTS_NAME string = monitoring.outputs.applicationInsightsN
output AZURE_CONTAINER_ENVIRONMENT_NAME string = containerApps.outputs.environmentName
output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerApps.outputs.registryLoginServer
output AZURE_CONTAINER_REGISTRY_NAME string = containerApps.outputs.registryName
output SERVICE_API_NAME string = challenge1.outputs.SERVICE_API_NAME
output SERVICE_NAME_CHALLENGE1 string = challenge1.outputs.SERVICE_API_NAME
// output SERVICE_NAME_CHALLENGE2 string = challenge2.outputs.SERVICE_API_NAME
9 changes: 0 additions & 9 deletions infra/main.parameters.json
Original file line number Diff line number Diff line change
@@ -7,15 +7,6 @@
},
"location": {
"value": "${AZURE_LOCATION}"
},
"openaiApiKey": {
"value": "${AZURE_OPENAI_API_KEY}"
},
"openaiEndpoint": {
"value": "${AZURE_OPENAI_ENDPOINT}"
},
"completionDeploymentName": {
"value": "${AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME}"
}
}
}
11 changes: 10 additions & 1 deletion src-agents/challenge1/main.py
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
from pydantic import BaseModel
from enum import Enum
from openai import AzureOpenAI
from azure.identity import DeviceCodeCredential

app = FastAPI()

@@ -26,8 +27,16 @@ class Answer(BaseModel):
promptTokensUsed: int | None = None
completionTokensUsed: int | None = None

openai_access = ''

if "AZURE_TENANT_ID" in os.environ:
default_credential = DeviceCodeCredential(tenant_id=os.getenv("AZURE_TENANT_ID"))
openai_access = default_credential.get_token("https://cognitiveservices.azure.com/.default").token
else:
openai_access = os.getenv("AZURE_OPENAI_API_KEY")

client = AzureOpenAI(
api_key = os.getenv("AZURE_OPENAI_API_KEY"),
api_key = openai_access,
api_version = os.getenv("AZURE_OPENAI_VERSION"),
azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
)
3 changes: 2 additions & 1 deletion src-agents/challenge1/requirements.txt
Original file line number Diff line number Diff line change
@@ -2,4 +2,5 @@ fastapi==0.110.0
uvicorn==0.23.2
pydantic==2.6.4
openai==1.14.2
python-dotenv==1.0.0
python-dotenv==1.0.0
azure-identity==1.15.0
7 changes: 7 additions & 0 deletions src-agents/challenge2/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM python:3.11

COPY . .

RUN pip install --no-cache-dir --upgrade -r requirements.txt

CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port ${PORT:-${WEBSITES_PORT:-8080}}"]
67 changes: 67 additions & 0 deletions src-agents/challenge2/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import os
from dotenv import load_dotenv
from fastapi import FastAPI
from pydantic import BaseModel
from enum import Enum
from openai import AzureOpenAI

app = FastAPI()

load_dotenv()

class QuestionType(str, Enum):
multiple_choice = "multiple_choice"
true_false = "true_false"
popular_choice = "popular_choice"
estimation = "estimation"

class Ask(BaseModel):
question: str | None = None
type: QuestionType
correlationToken: str | None = None

class Answer(BaseModel):
answer: str
correlationToken: str | None = None
promptTokensUsed: int | None = None
completionTokensUsed: int | None = None

client = AzureOpenAI(
api_key = os.getenv("AZURE_OPENAI_API_KEY"),
api_version = os.getenv("AZURE_OPENAI_VERSION"),
azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
)
deployment_name = os.getenv("AZURE_OPENAI_COMPLETION_DEPLOYMENT_NAME")

@app.get("/")
async def root():
return {"message": "Hello Smorgs"}

@app.get("/healthz", summary="Health check", operation_id="healthz")
async def get_products(query: str = None):
"""
Returns a status of the app
"""

@app.post("/ask", summary="Ask a question", operation_id="ask")
async def ask_question(ask: Ask):
"""
Ask a question
"""

# Send a completion call to generate an answer
print('Sending a request to openai')
start_phrase = ask.question
response = client.chat.completions.create(
model = deployment_name,
messages = [{"role" : "assistant", "content" : start_phrase}],
)

print(response.choices[0].message.content)
print(response)
answer = Answer(answer=response.choices[0].message.content)
answer.correlationToken = ask.correlationToken
answer.promptTokensUsed = response.usage.prompt_tokens
answer.completionTokensUsed = response.usage.completion_tokens

return answer
6 changes: 6 additions & 0 deletions src-agents/challenge2/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fastapi==0.110.0
uvicorn==0.23.2
pydantic==2.6.4
openai==1.14.2
python-dotenv==1.0.0
azure-identity==1.15.0

0 comments on commit e6d12da

Please sign in to comment.