-
Notifications
You must be signed in to change notification settings - Fork 16
Deploy sample application in container app - Python #89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7c1cbc9
d81af2c
c3c1e74
1d90369
39a239c
6f236de
4cbd949
1c0b548
ea11209
9b124ba
e99aa17
d221266
e733e9c
45fffa8
0b9adea
db60215
1b01b67
7716ce5
75e3a7d
cadc62f
d521cf1
fea98e1
51edfde
45854e9
35b4a33
224b2dc
2398b60
000ff20
348b4a9
6c78dbe
292c85c
a5b3168
e8dccd5
42e7d80
df4e892
03f59e9
4112c7a
2430fd0
684a02c
251c405
acc29c9
8d931c9
0971787
7bbfda7
df82639
f473a8a
004f8a2
6dc7d20
9e8d76d
ffaa2ea
132d1c3
9794391
1611d2e
c086ffc
c44f444
805f9ae
aa4c676
29c31bf
764ec3b
4083f53
7b293c3
0af6f5c
462149b
889e04f
1e660f6
fb91b81
d66f523
447ad69
a7ddfd7
739305f
6c7b64f
b424420
7aedf72
11e4b91
147dac1
5a25573
6769aa0
acb5b3d
99cba82
16a6f7a
910fa5a
ca7662d
e90a8c0
d8207dc
8f9364a
12b29e6
75fafb2
c970c26
3e42674
5e16ae4
730d539
714b7c6
71f385d
8aab1a5
7af4c80
1a333e4
2263d77
f2a1256
5de1417
80f6f1f
a0decdb
56ccfca
8b343c5
3d69c71
fb7b831
7009477
e700d23
dcb80b5
327f3c4
df325b9
e890c04
b8cde9b
75084ac
9b3dab9
16c348c
7f16967
b261a8d
70d4f03
d3ca65d
808d12b
003451b
a55f300
a18f5a9
272e2dd
4109e8c
4c73ead
daef7fa
f087043
d4ddc70
bbcd192
3e9688e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,277 @@ | ||
| name: Deploy Python Agent Framework to Azure Container Apps | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - users/tirthdoshi/local-playground | ||
| paths: | ||
| - 'python/agent-framework/sample-agent/**' | ||
| - '.github/workflows/docker-container-sampleagent-python.yml' | ||
| pull_request: | ||
| branches: | ||
| - main | ||
| paths: | ||
| - 'python/agent-framework/sample-agent/**' | ||
| - '.github/workflows/docker-container-sampleagent-python.yml' | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| id-token: write # Required for OIDC authentication | ||
| contents: read | ||
| pull-requests: write # Required to comment on PRs | ||
|
|
||
| env: | ||
| AZURE_RESOURCE_GROUP: agent365-samples-rg | ||
| ACR_NAME: agent365samplesacr | ||
| CONTAINER_APP_NAME: agent-framework-python | ||
| CONTAINER_APP_ENV: agent365-env | ||
| IMAGE_NAME: agent-framework-python | ||
|
|
||
| jobs: | ||
| build-and-deploy: | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Azure Login with Service Principal | ||
| uses: azure/login@v2 | ||
| with: | ||
| creds: ${{ secrets.AZURE_CREDENTIALS }} | ||
|
|
||
| - name: Create Resource Group if needed | ||
| run: | | ||
| if ! az group exists --name ${{ env.AZURE_RESOURCE_GROUP }} --output tsv | grep -q true; then | ||
| echo "Creating Resource Group..." | ||
| az group create --name ${{ env.AZURE_RESOURCE_GROUP }} --location eastus | ||
| fi | ||
|
|
||
| - name: Create ACR if needed | ||
| run: | | ||
| if ! az acr show --name ${{ env.ACR_NAME }} &> /dev/null; then | ||
| echo "Creating ACR..." | ||
| az acr create \ | ||
| --resource-group ${{ env.AZURE_RESOURCE_GROUP }} \ | ||
| --name ${{ env.ACR_NAME }} \ | ||
| --sku Basic \ | ||
| --admin-enabled true | ||
| fi | ||
|
|
||
| - name: Login to Azure Container Registry | ||
| run: | | ||
| az acr login --name ${{ env.ACR_NAME }} | ||
|
|
||
| - name: Build Docker Image | ||
| run: | | ||
| docker build \ | ||
| -t ${{ env.ACR_NAME }}.azurecr.io/${{ env.IMAGE_NAME }}:${{ github.sha }} \ | ||
| -t ${{ env.ACR_NAME }}.azurecr.io/${{ env.IMAGE_NAME }}:latest \ | ||
| -f python/agent-framework/sample-agent/Dockerfile \ | ||
| python/agent-framework/sample-agent | ||
|
|
||
| - name: Push Docker Image to ACR | ||
| run: | | ||
| docker push ${{ env.ACR_NAME }}.azurecr.io/${{ env.IMAGE_NAME }}:${{ github.sha }} | ||
| docker push ${{ env.ACR_NAME }}.azurecr.io/${{ env.IMAGE_NAME }}:latest | ||
|
|
||
| - name: Create Container App Environment if needed | ||
| run: | | ||
| if ! az containerapp env show --name ${{ env.CONTAINER_APP_ENV }} --resource-group ${{ env.AZURE_RESOURCE_GROUP }} &> /dev/null; then | ||
| echo "Creating Container App Environment..." | ||
| az containerapp env create \ | ||
| --name ${{ env.CONTAINER_APP_ENV }} \ | ||
| --resource-group ${{ env.AZURE_RESOURCE_GROUP }} \ | ||
| --location eastus | ||
| fi | ||
|
|
||
| - name: Deploy to Azure Container App | ||
| run: | | ||
| if az containerapp show --name ${{ env.CONTAINER_APP_NAME }} --resource-group ${{ env.AZURE_RESOURCE_GROUP }} &> /dev/null; then | ||
| echo "Updating existing Container App..." | ||
| az containerapp update \ | ||
| --name ${{ env.CONTAINER_APP_NAME }} \ | ||
| --resource-group ${{ env.AZURE_RESOURCE_GROUP }} \ | ||
| --image ${{ env.ACR_NAME }}.azurecr.io/${{ env.IMAGE_NAME }}:${{ github.sha }} \ | ||
| --set-env-vars \ | ||
| PORT=3978 \ | ||
| AZURE_OPENAI_ENDPOINT=${{ secrets.AZURE_OPENAI_ENDPOINT }} \ | ||
| AZURE_OPENAI_DEPLOYMENT=${{ secrets.AZURE_OPENAI_DEPLOYMENT }} \ | ||
| AZURE_OPENAI_API_VERSION=${{ secrets.AZURE_OPENAI_API_VERSION }} \ | ||
| AZURE_OPENAI_API_KEY=${{ secrets.AZURE_OPENAI_API_KEY }} \ | ||
| USE_AGENTIC_AUTH=true \ | ||
| ENABLE_OBSERVABILITY=true \ | ||
| ENABLE_OTEL=true \ | ||
| ENABLE_SENSITIVE_DATA=true \ | ||
| PYTHON_ENVIRONMENT=production \ | ||
| ENABLE_APPLICATION_INSIGHTS=${{ secrets.ENABLE_APPLICATION_INSIGHTS || 'false' }} \ | ||
| APPLICATIONINSIGHTS_CONNECTION_STRING=${{ secrets.APPLICATIONINSIGHTS_CONNECTION_STRING || '' }} \ | ||
| CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=${{ secrets.SERVICE_CONNECTION_CLIENT_ID }} \ | ||
| CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=${{ secrets.SERVICE_CONNECTION_CLIENT_SECRET }} \ | ||
| CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=${{ secrets.SERVICE_CONNECTION_TENANT_ID }} \ | ||
| CONNECTIONS__SERVICE_CONNECTION__SETTINGS__SCOPES=${{ secrets.SERVICE_CONNECTION_SCOPES || 'https://graph.microsoft.com/.default' }} \ | ||
| AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__AGENTIC__SETTINGS__TYPE=AgenticUserAuthorization \ | ||
| AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__AGENTIC__SETTINGS__SCOPES=${{ secrets.AGENTIC_SCOPES || 'https://graph.microsoft.com/.default' }} \ | ||
| AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__AGENTIC__SETTINGS__ALTERNATEBLUEPRINTCONNECTIONNAME=${{ secrets.AGENTIC_CONNECTION_NAME || 'https://graph.microsoft.com/.default' }} \ | ||
| CONNECTIONSMAP_0_SERVICEURL='*' \ | ||
| CONNECTIONSMAP_0_CONNECTION=SERVICE_CONNECTION | ||
| else | ||
| echo "Creating new Container App..." | ||
| az containerapp create \ | ||
| --name ${{ env.CONTAINER_APP_NAME }} \ | ||
| --resource-group ${{ env.AZURE_RESOURCE_GROUP }} \ | ||
| --environment ${{ env.CONTAINER_APP_ENV }} \ | ||
| --image ${{ env.ACR_NAME }}.azurecr.io/${{ env.IMAGE_NAME }}:${{ github.sha }} \ | ||
| --registry-server ${{ env.ACR_NAME }}.azurecr.io \ | ||
| --target-port 3978 \ | ||
| --ingress external \ | ||
| --min-replicas 1 \ | ||
| --max-replicas 3 \ | ||
| --cpu 0.5 \ | ||
| --memory 1.0Gi \ | ||
| --env-vars \ | ||
| PORT=3978 \ | ||
| AZURE_OPENAI_ENDPOINT=${{ secrets.AZURE_OPENAI_ENDPOINT }} \ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if someone wants to use some other llm?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good point Rahul. The purpose of this workflow is help users test their changes without having to deploy any models. That's the reason of defaulting to one. If one wants to use another model, the best way is locally running in that case. |
||
| AZURE_OPENAI_DEPLOYMENT=${{ secrets.AZURE_OPENAI_DEPLOYMENT }} \ | ||
| AZURE_OPENAI_API_VERSION=${{ secrets.AZURE_OPENAI_API_VERSION }} \ | ||
| AZURE_OPENAI_API_KEY=${{ secrets.AZURE_OPENAI_API_KEY }} \ | ||
| USE_AGENTIC_AUTH=true \ | ||
| ENABLE_OBSERVABILITY=true \ | ||
| ENABLE_OTEL=true \ | ||
| ENABLE_SENSITIVE_DATA=true \ | ||
| PYTHON_ENVIRONMENT=production \ | ||
| ENABLE_APPLICATION_INSIGHTS=${{ secrets.ENABLE_APPLICATION_INSIGHTS || 'false' }} \ | ||
| APPLICATIONINSIGHTS_CONNECTION_STRING=${{ secrets.APPLICATIONINSIGHTS_CONNECTION_STRING || '' }} \ | ||
| CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=${{ secrets.SERVICE_CONNECTION_CLIENT_ID }} \ | ||
| CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=${{ secrets.SERVICE_CONNECTION_CLIENT_SECRET }} \ | ||
| CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=${{ secrets.SERVICE_CONNECTION_TENANT_ID }} \ | ||
| CONNECTIONS__SERVICE_CONNECTION__SETTINGS__SCOPES=${{ secrets.SERVICE_CONNECTION_SCOPES || 'https://graph.microsoft.com/.default' }} \ | ||
| AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__AGENTIC__SETTINGS__TYPE=AgenticUserAuthorization \ | ||
| AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__AGENTIC__SETTINGS__SCOPES=${{ secrets.AGENTIC_SCOPES || 'https://graph.microsoft.com/.default' }} \ | ||
| AGENTAPPLICATION__USERAUTHORIZATION__HANDLERS__AGENTIC__SETTINGS__ALTERNATEBLUEPRINTCONNECTIONNAME=${{ secrets.AGENTIC_CONNECTION_NAME || 'https://graph.microsoft.com/.default' }} \ | ||
| CONNECTIONSMAP_0_SERVICEURL='*' \ | ||
| CONNECTIONSMAP_0_CONNECTION=SERVICE_CONNECTION | ||
| fi | ||
|
|
||
| - name: Get Container App URL | ||
| id: get-url | ||
| run: | | ||
| FQDN=$(az containerapp show \ | ||
| --name ${{ env.CONTAINER_APP_NAME }} \ | ||
| --resource-group ${{ env.AZURE_RESOURCE_GROUP }} \ | ||
| --query properties.configuration.ingress.fqdn \ | ||
| --output tsv) | ||
| echo "🚀 Container App deployed successfully!" | ||
| echo "🌐 URL: https://$FQDN" | ||
| echo "📍 Health: https://$FQDN/api/health" | ||
| echo "📨 Messages: https://$FQDN/api/messages" | ||
| echo "app_url=https://$FQDN" >> $GITHUB_OUTPUT | ||
| echo "health_url=https://$FQDN/api/health" >> $GITHUB_OUTPUT | ||
| echo "messages_url=https://$FQDN/api/messages" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: View Container App Logs | ||
| run: | | ||
| echo "📋 Fetching recent logs..." | ||
| az containerapp logs show \ | ||
| --name ${{ env.CONTAINER_APP_NAME }} \ | ||
| --resource-group ${{ env.AZURE_RESOURCE_GROUP }} \ | ||
| --tail 50 \ | ||
| --follow false | ||
|
|
||
| - name: Comment on PR with Deployment URL | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not fully following the this yml. Is the expectation to deploy this sample from this repo itself? What is the use case and business need? What if someone wants to use non-azure infrastructure?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah apologies, I might have not specified the context. We are working on an AI first project that needs verification of the packages. For example, if copilot makes any changes to the sdk, we want to ensure that the changes don't really affect the execution of the sdk. This workflow helps to verify that the changes are deployed and verified correctly.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In that case, I don't think this is a right approach to validate the SDK. There is an entire E2E integration test feature being built right now that deploys the samples to the resource group and test different areas. In your case, you are just checking if the app is healthy or not, but that does not guarantee if the application components where changes were made is correct. Besides that, you are exposing the secrets in the pipeline in OSS, which any PR can expose secrets to outside world(obviously they will fork, but still there are number of reason we should avoid secrets in pipeline) |
||
| if: github.event_name == 'pull_request' | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const appUrl = '${{ steps.get-url.outputs.app_url }}'; | ||
| const healthUrl = '${{ steps.get-url.outputs.health_url }}'; | ||
| const messagesUrl = '${{ steps.get-url.outputs.messages_url }}'; | ||
| const sha = '${{ github.sha }}'; | ||
|
|
||
| const body = `## 🚀 Deployment Successful! | ||
|
|
||
| Your Python Agent Framework has been deployed to Azure Container Apps. | ||
|
|
||
| ### 🔗 Deployment Links | ||
| - **App URL**: ${appUrl} | ||
| - **Health Endpoint**: ${healthUrl} | ||
| - **Messages Endpoint**: ${messagesUrl} | ||
|
|
||
| ### 📦 Deployment Details | ||
| - **Container App**: \`${{ env.CONTAINER_APP_NAME }}\` | ||
| - **Resource Group**: \`${{ env.AZURE_RESOURCE_GROUP }}\` | ||
| - **Image Tag**: \`${sha.substring(0, 7)}\` | ||
| - **Commit**: ${sha} | ||
|
|
||
| ### 🧪 Testing with Agents Playground | ||
|
|
||
| #### Option 1: Direct Testing (Simple) | ||
| \`\`\`bash | ||
| # Check health | ||
| curl ${healthUrl} | ||
|
|
||
| # Send a test message (requires authentication) | ||
| curl -X POST ${messagesUrl} \\ | ||
| -H "Content-Type: application/json" \\ | ||
| -d '{"type":"message","text":"Hello Agent!"}' | ||
| \`\`\` | ||
|
|
||
| #### Option 2: Test with Agents Playground (Interactive) | ||
|
|
||
| 1. **Install and authenticate ngrok:** | ||
| \`\`\`bash | ||
| # Download ngrok from https://ngrok.com/download | ||
| # Authenticate with your token | ||
| ngrok authtoken YOUR_NGROK_TOKEN | ||
| \`\`\` | ||
|
|
||
| 2. **Start ngrok tunnel:** | ||
| \`\`\`bash | ||
| ngrok http 2000 | ||
| \`\`\` | ||
| Copy the HTTPS forwarding URL (e.g., \`https://abc123.ngrok.io\`) | ||
|
|
||
| 3. **Launch Agents Playground:** | ||
| \`\`\`bash | ||
| agentsplayground -p 2000 -e ${messagesUrl} --su 'YOUR_NGROK_URL/_connector' | ||
| \`\`\` | ||
|
|
||
| Replace \`YOUR_NGROK_URL\` with the ngrok URL from step 2. | ||
|
|
||
| 4. **Test your agent** in the playground UI at http://localhost:2000 | ||
|
|
||
| --- | ||
| *Deployed from commit ${sha.substring(0, 7)} by @${{ github.actor }}*`; | ||
|
|
||
| // Find existing comment from this bot | ||
| const comments = await github.rest.issues.listComments({ | ||
| issue_number: context.issue.number, | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| }); | ||
|
|
||
| const botComment = comments.data.find(comment => | ||
| comment.user.type === 'Bot' && | ||
| comment.body.includes('🚀 Deployment Successful!') | ||
| ); | ||
|
|
||
| if (botComment) { | ||
| // Update existing comment | ||
| await github.rest.issues.updateComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| comment_id: botComment.id, | ||
| body: body | ||
| }); | ||
| console.log('Updated existing comment'); | ||
| } else { | ||
| // Create new comment | ||
| await github.rest.issues.createComment({ | ||
| issue_number: context.issue.number, | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| body: body | ||
| }); | ||
| console.log('Created new comment'); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # Use Python 3.11 full Debian image | ||
| FROM python:3.11 | ||
|
|
||
| # Set working directory | ||
| WORKDIR /app | ||
|
|
||
| # Install system dependencies | ||
| RUN apt-get update && apt-get install -y \ | ||
| curl \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| # Copy project files | ||
| COPY pyproject.toml ./ | ||
| COPY *.py ./ | ||
| COPY .env* ./ | ||
|
|
||
| # Install uv | ||
| RUN pip install --no-cache-dir uv | ||
|
|
||
| # Install dependencies | ||
| RUN uv pip install --system --pre -e . | ||
|
|
||
| EXPOSE 3978 | ||
| ENV PORT=3978 | ||
|
|
||
| CMD ["python", "start_with_generic_host.py"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this has to be in .github workflow folders? This sounds like it's specific to agent framework. Shouldn't it be moved to sample agent folder?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it is specific to the agent framework. Since it's a github actions workflow it needs to be under .github. That's the reason to keep the path :

So it will only run on those specific changes.