├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── foundation ├── README.md ├── aistudio-infra │ ├── README.md │ ├── assets │ │ ├── AIStudioComponents.png │ │ ├── aiproject.png │ │ ├── aistudio-vnet.png │ │ ├── modelregions.png │ │ └── pftest.png │ ├── bicep │ │ ├── main-local.bicepparam │ │ ├── main.bicep │ │ ├── main.bicepparam │ │ └── modules │ │ │ ├── ai-hub.bicep │ │ │ ├── dependent-resources.bicep │ │ │ └── dependent │ │ │ ├── aiservices.bicep │ │ │ ├── applicationinsights.bicep │ │ │ ├── containerregistry.bicep │ │ │ ├── keyvault.bicep │ │ │ └── storage.bicep │ ├── linux-env-setup.md │ ├── scripts │ │ ├── aistudio_create_connections.py │ │ ├── get_ipconfig.sh │ │ ├── provision_ai_studio.sh │ │ ├── requirements.txt │ │ └── setup_python_env.sh │ ├── terraform │ │ ├── .terraform.lock.hcl │ │ ├── main.tf │ │ ├── modules │ │ │ ├── ai │ │ │ │ ├── mlw │ │ │ │ │ ├── main.tf │ │ │ │ │ ├── outputs.tf │ │ │ │ │ ├── providers.tf │ │ │ │ │ └── variables.tf │ │ │ │ └── search │ │ │ │ │ ├── main.tf │ │ │ │ │ ├── outputs.tf │ │ │ │ │ └── variables.tf │ │ │ ├── containers │ │ │ │ └── cr │ │ │ │ │ ├── main.tf │ │ │ │ │ ├── outputs.tf │ │ │ │ │ └── variables.tf │ │ │ ├── core │ │ │ │ ├── pe │ │ │ │ │ ├── main.tf │ │ │ │ │ └── variables.tf │ │ │ │ └── st │ │ │ │ │ ├── main.tf │ │ │ │ │ ├── outputs.tf │ │ │ │ │ └── variables.tf │ │ │ ├── observability │ │ │ │ ├── appi │ │ │ │ │ ├── main.tf │ │ │ │ │ ├── outputs.tf │ │ │ │ │ └── variables.tf │ │ │ │ └── logs │ │ │ │ │ ├── main.tf │ │ │ │ │ ├── outputs.tf │ │ │ │ │ └── variables.tf │ │ │ └── security │ │ │ │ └── kv │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ ├── providers.tf │ │ └── variables.tf │ └── tests │ │ ├── ai-service-chat-test.ipynb │ │ ├── ai-service-sdk-test.ipynb │ │ ├── pftest.sh │ │ └── pftest_chatbot │ │ ├── .gitignore │ │ ├── .promptflow │ │ └── flow.tools.json │ │ ├── README.md │ │ ├── azure_ai_services.yaml │ │ ├── azure_openai.yaml │ │ ├── chat.jinja2 │ │ ├── flow.dag.yaml │ │ ├── openai.yaml │ │ ├── pip1.txt │ │ ├── pip2.txt │ │ └── requirements.txt ├── aoai-infra │ ├── README.md │ ├── bicep │ │ ├── aoai.bicep │ │ ├── aoairole.bicep │ │ ├── main.bicep │ │ ├── main.bicepparam │ │ ├── main.bicepparam.json │ │ ├── privateendpoint.bicep │ │ └── userPrincipalIds.json │ ├── terraform │ │ ├── .terraform.lock.hcl │ │ ├── main.tf │ │ ├── modules │ │ │ ├── ai │ │ │ │ └── cognitive │ │ │ │ │ ├── main.tf │ │ │ │ │ ├── outputs.tf │ │ │ │ │ └── variables.tf │ │ │ └── security │ │ │ │ └── rbac │ │ │ │ ├── main.tf │ │ │ │ └── variables.tf │ │ ├── providers.tf │ │ └── variables.tf │ ├── test.ps1 │ ├── test.sh │ └── test_template.json ├── apimv2-infra │ ├── README.md │ ├── assets │ │ ├── apim_endpoint_1.png │ │ ├── apim_key_1.png │ │ └── endpoint_and_key_mapping.png │ ├── bicep │ │ ├── apimv2.bicep │ │ ├── apimv2.bicepparam │ │ ├── artifacts │ │ │ └── openAIOpenAPI.json │ │ └── modules │ │ │ ├── apimService.bicep │ │ │ ├── apimServiceConfiguration.bicep │ │ │ ├── appinsights.bicep │ │ │ ├── eventhub.bicep │ │ │ ├── keyvault.bicep │ │ │ ├── loganalytics.bicep │ │ │ ├── roleAssignment.bicep │ │ │ ├── storageaccount.bicep │ │ │ └── streamanalytics.bicep │ ├── endpoint_test.ipynb │ ├── endpoint_test.py │ └── terraform │ │ ├── .terraform.lock.hcl │ │ ├── main.tf │ │ ├── modules │ │ ├── core │ │ │ └── st │ │ │ │ ├── main.tf │ │ │ │ └── variables.tf │ │ ├── integration │ │ │ ├── apim │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ ├── providers.tf │ │ │ │ └── variables.tf │ │ │ ├── apim_configuration │ │ │ │ ├── main.tf │ │ │ │ ├── providers.tf │ │ │ │ └── variables.tf │ │ │ ├── asa │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ │ └── evh │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ ├── observability │ │ │ ├── appi │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ │ └── logs │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ └── security │ │ │ ├── kv │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ │ └── rbac │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ ├── providers.tf │ │ └── variables.tf └── standalone │ ├── README.md │ ├── app │ ├── README.md │ ├── assets │ │ └── enterprise-gpt.png │ ├── backend │ │ ├── .gitignore │ │ ├── .python-version │ │ ├── .vscode │ │ │ └── extensions.json │ │ ├── FlaskApp │ │ │ ├── __init__.py │ │ │ ├── approaches │ │ │ │ ├── __init__.py │ │ │ │ ├── approach.py │ │ │ │ ├── chatreadretrieveread.py │ │ │ │ ├── readdecomposeask.py │ │ │ │ ├── readretrieveread.py │ │ │ │ └── retrievethenread.py │ │ │ ├── clients.py │ │ │ ├── cog_services.py │ │ │ ├── data │ │ │ │ └── employeeinfo.csv │ │ │ ├── langchainadapters.py │ │ │ ├── lookuptool.py │ │ │ ├── requirements.txt │ │ │ └── text.py │ │ ├── HandleApproach │ │ │ ├── __init__.py │ │ │ └── function.json │ │ ├── getting_started.md │ │ ├── host.json │ │ └── requirements.txt │ ├── frontend │ │ ├── .gitignore │ │ ├── .prettierrc.json │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── vite.svg │ │ ├── src │ │ │ ├── api │ │ │ │ ├── api.ts │ │ │ │ ├── index.ts │ │ │ │ └── models.ts │ │ │ ├── assets │ │ │ │ ├── github.svg │ │ │ │ ├── react.svg │ │ │ │ └── search.svg │ │ │ ├── components │ │ │ │ ├── AnalysisPanel │ │ │ │ │ ├── AnalysisPanel.module.css │ │ │ │ │ ├── AnalysisPanel.tsx │ │ │ │ │ ├── AnalysisPanelTabs.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── Answer │ │ │ │ │ ├── Answer.module.css │ │ │ │ │ ├── Answer.tsx │ │ │ │ │ ├── AnswerError.tsx │ │ │ │ │ ├── AnswerIcon.tsx │ │ │ │ │ ├── AnswerLoading.tsx │ │ │ │ │ ├── AnswerParser.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── ClearChatButton │ │ │ │ │ ├── ClearChatButton.module.css │ │ │ │ │ ├── ClearChatButton.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── Example │ │ │ │ │ ├── Example.module.css │ │ │ │ │ ├── Example.tsx │ │ │ │ │ ├── ExampleList.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── QuestionInput │ │ │ │ │ ├── QuestionInput.module.css │ │ │ │ │ ├── QuestionInput.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SettingsButton │ │ │ │ │ ├── SettingsButton.module.css │ │ │ │ │ ├── SettingsButton.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── SupportingContent │ │ │ │ │ ├── SupportingContent.module.css │ │ │ │ │ ├── SupportingContent.tsx │ │ │ │ │ ├── SupportingContentParser.ts │ │ │ │ │ └── index.ts │ │ │ │ └── UserChatMessage │ │ │ │ │ ├── UserChatMessage.module.css │ │ │ │ │ ├── UserChatMessage.tsx │ │ │ │ │ └── index.ts │ │ │ ├── index.css │ │ │ ├── index.tsx │ │ │ ├── pages │ │ │ │ ├── NoPage.tsx │ │ │ │ ├── chat │ │ │ │ │ ├── Chat.module.css │ │ │ │ │ └── Chat.tsx │ │ │ │ ├── layout │ │ │ │ │ ├── Layout.module.css │ │ │ │ │ └── Layout.tsx │ │ │ │ ├── oneshot │ │ │ │ │ ├── OneShot.module.css │ │ │ │ │ └── OneShot.tsx │ │ │ │ └── uploadfile │ │ │ │ │ ├── FileUploader.module.css │ │ │ │ │ └── FileUploader.tsx │ │ │ └── vite-env.d.ts │ │ ├── staticwebapp.config.json │ │ ├── swa-cli.config.json │ │ ├── tsconfig.json │ │ └── vite.config.ts │ └── test_document │ │ ├── Invoice-6.pdf │ │ └── Invoice_1.pdf │ ├── assets │ └── azure-open-ai-standalone-landing-zone.jpg │ ├── bicep │ ├── ai │ │ ├── cognitive.bicep │ │ └── search.bicep │ ├── core │ │ ├── storage.bicep │ │ └── vnet.bicep │ ├── host │ │ ├── appservice.bicep │ │ ├── appserviceplan.bicep │ │ ├── azfunctions.bicep │ │ └── staticwebsite.bicep │ ├── main.bicep │ ├── main.bicepparam │ ├── main.json │ ├── main.parameters.json │ └── security │ │ ├── bastion.bicep │ │ ├── install-tools-windows.ps1 │ │ ├── keyvault.bicep │ │ ├── password-generator.bicep │ │ └── rbac.bicep │ ├── terraform │ ├── .terraform.lock.hcl │ ├── main.tf │ ├── modules │ │ ├── ai │ │ │ ├── cognitive │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ │ └── search │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ ├── core │ │ │ ├── st │ │ │ │ ├── main.tf │ │ │ │ └── variables.tf │ │ │ └── vnet │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ ├── host │ │ │ ├── appi │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ │ ├── asp │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ │ ├── func │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ │ └── stapp │ │ │ │ ├── main.tf │ │ │ │ ├── providers.tf │ │ │ │ └── variables.tf │ │ └── security │ │ │ ├── bastion │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ │ ├── jumpbox │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ │ ├── kv │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ │ ├── nsg │ │ │ └── .gitkeep │ │ │ └── rbac │ │ │ ├── main.tf │ │ │ └── variables.tf │ ├── providers.tf │ └── variables.tf │ └── tests │ ├── ai-service-chat-test.ipynb │ ├── ai-service-sdk-test.ipynb │ ├── pftest.sh │ └── pftest_chatbot │ ├── .gitignore │ ├── .promptflow │ └── flow.tools.json │ ├── README.md │ ├── azure_openai.yaml │ ├── chat.jinja2 │ ├── flow.dag.yaml │ ├── openai.yaml │ └── requirements.txt ├── media ├── RAG_Components.png ├── RAG_Workflow.png └── gen_ai_use_cases.png └── scenarios ├── autonomous_agent └── README.md ├── data_driven_decisioning └── README.md ├── rag └── README.md └── summarization └── README.md /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # TODO: The maintainer of this repo has not yet edited this file 2 | 3 | **REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? 4 | 5 | - **No CSS support:** Fill out this template with information about how to file issues and get help. 6 | - **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps. 7 | - **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide. 8 | 9 | *Then remove this first heading from this SUPPORT.MD file before publishing your repo.* 10 | 11 | # Support 12 | 13 | ## How to file issues and get help 14 | 15 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 16 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 17 | feature request as a new Issue. 18 | 19 | For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE 20 | FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER 21 | CHANNEL. WHERE WILL YOU HELP PEOPLE?**. 22 | 23 | ## Microsoft Support Policy 24 | 25 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above. 26 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/assets/AIStudioComponents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-openai-landing-zone/e5a785144593b00ac9fba58322a1651ff5d9b06e/foundation/aistudio-infra/assets/AIStudioComponents.png -------------------------------------------------------------------------------- /foundation/aistudio-infra/assets/aiproject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-openai-landing-zone/e5a785144593b00ac9fba58322a1651ff5d9b06e/foundation/aistudio-infra/assets/aiproject.png -------------------------------------------------------------------------------- /foundation/aistudio-infra/assets/aistudio-vnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-openai-landing-zone/e5a785144593b00ac9fba58322a1651ff5d9b06e/foundation/aistudio-infra/assets/aistudio-vnet.png -------------------------------------------------------------------------------- /foundation/aistudio-infra/assets/modelregions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-openai-landing-zone/e5a785144593b00ac9fba58322a1651ff5d9b06e/foundation/aistudio-infra/assets/modelregions.png -------------------------------------------------------------------------------- /foundation/aistudio-infra/assets/pftest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-openai-landing-zone/e5a785144593b00ac9fba58322a1651ff5d9b06e/foundation/aistudio-infra/assets/pftest.png -------------------------------------------------------------------------------- /foundation/aistudio-infra/bicep/main-local.bicepparam: -------------------------------------------------------------------------------- 1 | using './main.bicep' 2 | 3 | param aiHubName = 'lz-test1-r' 4 | param aiHubFriendlyName = 'lz-test1-r' 5 | param aiHubDescription = 'This is an example AI resource for use in Azure AI Studio.' 6 | param tags = {} 7 | param vnetName = 'vnet-westus' 8 | param vnetRgName = 'vnet' 9 | param subnetName = 'containerapp' 10 | param location = 'westus' 11 | param prefix = 'anildwa' 12 | 13 | param azureOpenAIModelName = 'gpt-4o' 14 | param azureOpenAIModelVersion = '2024-05-13' 15 | param modelDeploymentName = 'gpt-4o' 16 | param modelDeploymentCapacity = 1 //e.g 1 (1000s TPMs) 17 | 18 | //Private DNS Zones Parameters 19 | param blobPrivateDnsZoneRg = 'aml-rg' 20 | param filePrivateDnsZoneRg = 'vnet' 21 | param vaultPrivateDnsZoneRg = 'vnet' 22 | param azuremlApiPrivateDnsZoneRg = 'openai' 23 | param azurecrPrivateDnsZoneRg = 'vnet' 24 | param cognitiveServicesPrivateDnsZoneRg = 'vnet' 25 | param openAiPrivateDnsZoneRg = 'vnet' 26 | param notebooksPrivateDnsZoneRg = 'openai' 27 | 28 | 29 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/bicep/main.bicepparam: -------------------------------------------------------------------------------- 1 | using './main.bicep' 2 | 3 | param aiHubName = 'demo' 4 | param aiHubFriendlyName = 'Demo AI resource' 5 | param aiHubDescription = 'This is an example AI resource for use in Azure AI Studio.' 6 | param tags = {} 7 | param vnetName = 'your-vnet-name' 8 | param vnetRgName = 'your-vnet-resource-group' 9 | param subnetName = 'your-subnet-name' 10 | param location = 'your-location' 11 | param prefix = 'prefix' 12 | 13 | param azureOpenAIModelName = '' 14 | param azureOpenAIModelVersion = '2024-05-13' 15 | param modelDeploymentName = '' 16 | param modelDeploymentCapacity = 1 //e.g 1 (1000s TPMs) 17 | 18 | param blobPrivateDnsZoneRg = 'aml-rg' 19 | param filePrivateDnsZoneRg = 'vnet' 20 | param vaultPrivateDnsZoneRg = 'vnet' 21 | param azuremlApiPrivateDnsZoneRg = 'openai' 22 | param azurecrPrivateDnsZoneRg = 'vnet' 23 | param cognitiveServicesPrivateDnsZoneRg = 'vnet' 24 | param openAiPrivateDnsZoneRg = 'vnet' 25 | param notebooksPrivateDnsZoneRg = 'openai' 26 | 27 | 28 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/bicep/modules/dependent/applicationinsights.bicep: -------------------------------------------------------------------------------- 1 | // Creates an Application Insights instance as dependency for Azure ML 2 | @description('Azure region of the deployment') 3 | param location string = resourceGroup().location 4 | 5 | @description('Tags to add to the resources') 6 | param tags object = {} 7 | 8 | @description('Application Insights resource name') 9 | param applicationInsightsName string 10 | 11 | @description('Log Analytics resource name') 12 | param logAnalyticsWorkspaceName string 13 | 14 | resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { 15 | name: logAnalyticsWorkspaceName 16 | location: location 17 | properties: { 18 | sku: { 19 | name: 'PerGB2018' 20 | } 21 | retentionInDays: 30 22 | publicNetworkAccessForIngestion: 'Enabled' 23 | publicNetworkAccessForQuery: 'Disabled' 24 | } 25 | } 26 | 27 | resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { 28 | name: applicationInsightsName 29 | location: location 30 | tags: tags 31 | kind: 'web' 32 | properties: { 33 | Application_Type: 'web' 34 | WorkspaceResourceId: logAnalyticsWorkspace.id 35 | Flow_Type: 'Bluefield' 36 | } 37 | } 38 | 39 | output applicationInsightsId string = applicationInsights.id 40 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/bicep/modules/dependent/containerregistry.bicep: -------------------------------------------------------------------------------- 1 | // Creates an Azure Container Registry with Azure Private Link endpoint 2 | @description('Azure region of the deployment') 3 | param location string 4 | 5 | @description('Tags to add to the resources') 6 | param tags object 7 | 8 | @description('Container registry name') 9 | param containerRegistryName string 10 | 11 | @description('Container registry private link endpoint name') 12 | param containerRegistryPleName string 13 | 14 | @description('Resource ID of the subnet') 15 | param subnetId string 16 | 17 | @description('Resource group name for the Private DNS Zone for Azure Container Registry.') 18 | param azurecrPrivateDnsZoneRg string 19 | 20 | var containerRegistryNameCleaned = replace(containerRegistryName, '-', '') 21 | 22 | var privateDnsZoneName = 'privatelink${environment().suffixes.acrLoginServer}' 23 | 24 | var groupName = 'registry' 25 | 26 | resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-07-01' = { 27 | name: containerRegistryNameCleaned 28 | location: location 29 | tags: tags 30 | sku: { 31 | name: 'Premium' 32 | } 33 | properties: { 34 | adminUserEnabled: false 35 | dataEndpointEnabled: false 36 | networkRuleBypassOptions: 'AzureServices' 37 | networkRuleSet: { 38 | defaultAction: 'Deny' 39 | } 40 | policies: { 41 | quarantinePolicy: { 42 | status: 'disabled' 43 | } 44 | retentionPolicy: { 45 | status: 'enabled' 46 | days: 7 47 | } 48 | trustPolicy: { 49 | status: 'enabled' 50 | type: 'Notary' 51 | } 52 | } 53 | publicNetworkAccess: 'Disabled' 54 | zoneRedundancy: 'Disabled' 55 | } 56 | } 57 | 58 | resource containerRegistryPrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-11-01' = { 59 | name: containerRegistryPleName 60 | location: location 61 | tags: tags 62 | properties: { 63 | privateLinkServiceConnections: [ 64 | { 65 | name: containerRegistryPleName 66 | properties: { 67 | groupIds: [ 68 | groupName 69 | ] 70 | privateLinkServiceId: containerRegistry.id 71 | } 72 | } 73 | ] 74 | subnet: { 75 | id: subnetId 76 | } 77 | } 78 | } 79 | 80 | 81 | resource acrPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { 82 | name: privateDnsZoneName 83 | scope: resourceGroup(subscription().subscriptionId, azurecrPrivateDnsZoneRg) 84 | //location: 'global' 85 | } 86 | 87 | resource privateEndpointDns 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-11-01' = { 88 | parent: containerRegistryPrivateEndpoint 89 | name: '${groupName}-PrivateDnsZoneGroup' 90 | properties:{ 91 | privateDnsZoneConfigs: [ 92 | { 93 | name: privateDnsZoneName 94 | properties:{ 95 | privateDnsZoneId: acrPrivateDnsZone.id 96 | } 97 | } 98 | ] 99 | } 100 | } 101 | 102 | output containerRegistryId string = containerRegistry.id 103 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/bicep/modules/dependent/keyvault.bicep: -------------------------------------------------------------------------------- 1 | // Creates a KeyVault with Private Link Endpoint 2 | @description('The Azure Region to deploy the resources into') 3 | param location string = resourceGroup().location 4 | 5 | @description('Tags to apply to the Key Vault Instance') 6 | param tags object = {} 7 | 8 | @description('The name of the Key Vault') 9 | param keyvaultName string 10 | 11 | @description('The name of the Key Vault private link endpoint') 12 | param keyvaultPleName string 13 | 14 | @description('The Subnet ID where the Key Vault Private Link is to be created') 15 | param subnetId string 16 | 17 | @description('Resource group name for the Private DNS Zone for Vault.') 18 | param vaultPrivateDnsZoneRg string 19 | 20 | var privateDnsZoneName = 'privatelink.vaultcore.azure.net' 21 | 22 | resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = { 23 | name: keyvaultName 24 | location: location 25 | tags: tags 26 | properties: { 27 | createMode: 'default' 28 | enabledForDeployment: false 29 | enabledForDiskEncryption: false 30 | enabledForTemplateDeployment: false 31 | enableSoftDelete: true 32 | enableRbacAuthorization: true 33 | enablePurgeProtection: true 34 | networkAcls: { 35 | bypass: 'AzureServices' 36 | defaultAction: 'Deny' 37 | } 38 | sku: { 39 | family: 'A' 40 | name: 'standard' 41 | } 42 | softDeleteRetentionInDays: 7 43 | tenantId: subscription().tenantId 44 | } 45 | } 46 | 47 | resource keyVaultPrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-11-01' = { 48 | name: keyvaultPleName 49 | location: location 50 | tags: tags 51 | properties: { 52 | privateLinkServiceConnections: [ 53 | { 54 | name: keyvaultPleName 55 | properties: { 56 | groupIds: [ 57 | 'vault' 58 | ] 59 | privateLinkServiceId: keyVault.id 60 | } 61 | } 62 | ] 63 | subnet: { 64 | id: subnetId 65 | } 66 | } 67 | } 68 | 69 | resource keyVaultPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { 70 | name: privateDnsZoneName 71 | scope: resourceGroup(subscription().subscriptionId, vaultPrivateDnsZoneRg) 72 | //location: 'global' 73 | } 74 | 75 | resource privateEndpointDns 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-11-01' = { 76 | parent: keyVaultPrivateEndpoint 77 | name: 'vault-PrivateDnsZoneGroup' 78 | properties:{ 79 | privateDnsZoneConfigs: [ 80 | { 81 | name: privateDnsZoneName 82 | properties:{ 83 | privateDnsZoneId: keyVaultPrivateDnsZone.id 84 | } 85 | } 86 | ] 87 | } 88 | } 89 | 90 | output keyvaultId string = keyVault.id 91 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/linux-env-setup.md: -------------------------------------------------------------------------------- 1 | # Setting up a Linux environment 2 | 3 | If you are using a Linux environment to deploy this landing zone, follow these steps to setup your environment. Please note that these commands were used in a specific Ubuntu Linux 22.04.1. You can easily create a shell script and modify these to fit your own Linux distro. 4 | 5 | 1. **Update and Install Python** 6 | ```sh 7 | $ sudo apt update 8 | $ sudo apt install python3-pip 9 | ``` 10 | 11 | 2. **Verify Python Installation** 12 | ```sh 13 | $ python3 --version 14 | Python 3.12.4 15 | $ pip3 --version 16 | pip 24.2 from ~/miniconda3/lib/python3.12/site-packages/pip (python 3.12) 17 | ``` 18 | 19 | 3. **Install Miniconda** 20 | ```sh 21 | $ wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh 22 | $ chmod +x Miniconda3-latest-Linux-x86_64.sh 23 | $ ./Miniconda3-latest-Linux-x86_64.sh 24 | ``` 25 | 26 | 4. **Verify Miniconda Installation** 27 | ```sh 28 | $ conda --version 29 | conda 24.7.1 30 | ``` 31 | 32 | 5. **Create Conda Environment and Install Azure CLI** 33 | ```sh 34 | $ conda create -n python=3.10 35 | $ curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash 36 | ``` 37 | 38 | 6. **Verify Azure CLI Installation** 39 | ```sh 40 | $ az --version 41 | azure-cli 2.63.0 42 | ... 43 | ``` 44 | 45 | 7. **Install Additional Python Packages** 46 | ```sh 47 | $ pip install azure.identity 48 | $ pip install azure-mgmt-resource 49 | $ pip install keyring 50 | $ pip install keyrings.alt 51 | ``` 52 | 53 | 8. **Verify Python Package Installations** 54 | ```sh 55 | $ pip list | grep azure 56 | azure-common 1.1.28 57 | azure-core 1.30.2 58 | azure-identity 1.17.1 59 | azure-mgmt-core 1.4.0 60 | azure-mgmt-resource 23.1.1 61 | azure-monitor-opentelemetry-exporter 1.0.0b28 62 | ``` 63 | 64 | 9. **Install Promptflow and Verify Installation** 65 | ```sh 66 | $ pip install promptflow --upgrade 67 | $ pf --version 68 | { 69 | "promptflow": "1.15.0", 70 | "promptflow-core": "1.15.0", 71 | "promptflow-devkit": "1.15.0", 72 | "promptflow-tracing": "1.15.0" 73 | } 74 | ``` 75 | 76 | 10. **Continue with Deployment Steps** 77 | ```sh 78 | $ az login 79 | ``` 80 | ``` 81 | *IMPORTANT* 82 | Make sure you have a network connection to your VNet. -------------------------------------------------------------------------------- /foundation/aistudio-infra/scripts/aistudio_create_connections.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from azure.ai.resources.client import AIClient 3 | from azure.identity import DefaultAzureCredential 4 | from azure.ai.resources.entities import AzureAISearchConnection 5 | from azure.ai.ml.entities._credentials import ApiKeyConfiguration 6 | 7 | def create_ai_search_connection(subscription_id:str, resource_group_name: str, connection_name: str, ai_resource_name: str, ai_search_name: str, ai_search_key: str): 8 | ai_client = AIClient(credential=DefaultAzureCredential(), subscription_id=subscription_id, 9 | resource_group_name=resource_group_name, ai_resource_name=ai_resource_name) 10 | _connection_name = connection_name 11 | cred = ApiKeyConfiguration(key=ai_search_key) 12 | target = f"https://{ai_search_name}.search.windows.net" 13 | local_conn = AzureAISearchConnection(name="overwrite", credentials=None, target="overwrite") 14 | local_conn.name = _connection_name 15 | local_conn.credentials = cred 16 | local_conn.target = target 17 | ai_client.connections.create_or_update(local_conn) 18 | result_connection_name = ai_client.connections.get(_connection_name) 19 | if result_connection_name.name == _connection_name: 20 | print(f"{connection_name} Connection created successfully.") 21 | return(f"{connection_name} Connection created successfully.") 22 | 23 | def main(args): 24 | return create_ai_search_connection(args.subscription_id, args.resource_group_name,args.connection_name, args.ai_resource_name, args.ai_search_name, args.ai_search_key) 25 | 26 | if __name__ == "__main__": 27 | parser = argparse.ArgumentParser(description="Create Azure AI Studio Connections") 28 | parser.add_argument("--subscription_id", type=str, help="Azure Subscription ID") 29 | parser.add_argument("--resource_group_name", type=str, help="Azure Resource Group Name") 30 | parser.add_argument("--ai_resource_name", type=str, help="AI Resource Name") 31 | parser.add_argument("--connection_name", type=str, help="AI Search Connection Name") 32 | parser.add_argument("--ai_search_name", type=str, help="AI Search Service Name") 33 | parser.add_argument("--ai_search_key", type=str, help="AI Search Service Admin Key") 34 | args = parser.parse_args() 35 | main(args) 36 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/scripts/requirements.txt: -------------------------------------------------------------------------------- 1 | azure-identity 2 | azure-ai-generative[faiss] 3 | azure-search-documents 4 | azure-ai-resources 5 | azure-ai-ml 6 | promptflow 7 | promptflow-tools 8 | keyrings.alt -------------------------------------------------------------------------------- /foundation/aistudio-infra/scripts/setup_python_env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # check if conda is installed 4 | 5 | print_status() { 6 | echo -e "\e[32m$1\e[0m" 7 | } 8 | 9 | 10 | RESOURCE_GROUP_NAME=$1 11 | LOCATION=$2 12 | 13 | 14 | subscription_id=$(az account show | jq -r .id) 15 | ai_resource_name=$(az resource list -g $RESOURCE_GROUP_NAME -l $LOCATION --query "[?kind=='Hub']" | jq -r .[0].name) 16 | search_service_name=$(az resource list -g $RESOURCE_GROUP_NAME -l $LOCATION --query "[?type=='Microsoft.Search/searchServices']" | jq -r .[].name) 17 | aisearch_api_key=$(az search admin-key show -g $RESOURCE_GROUP_NAME --service-name $search_service_name | jq -r .primaryKey) 18 | ai_search_connection_name="${search_service_name}_connection_$(uuidgen)" 19 | 20 | CONDA_ENV_NAME="aoai-landingzone" 21 | 22 | if [ -x "$(command -v conda)" ]; then 23 | print_status "conda is installed" 24 | 25 | # check if conda environment exists 26 | if conda env list | grep -q "$CONDA_ENV_NAME"; then 27 | print_status "$CONDA_ENV_NAME environment exists" 28 | else 29 | print_status "$CONDA_ENV_NAME environment does not exist" 30 | conda create -n $CONDA_ENV_NAME python=3.10 -y 31 | fi 32 | else 33 | print_status "conda is not installed" 34 | print_status "Installing conda" 35 | wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh 36 | chmod +x ./Miniconda3-latest-Linux-x86_64.sh 37 | ./Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda 38 | export PATH="$HOME/miniconda/bin:$PATH" 39 | conda init bash 40 | source ~/.bashrc 41 | conda init 42 | conda config --set auto_activate_base false 43 | conda config --set always_yes yes 44 | conda update -n base -c defaults conda 45 | conda create -n $CONDA_ENV_NAME python=3.10 46 | fi 47 | 48 | 49 | 50 | source ~/miniconda3/etc/profile.d/conda.sh 51 | 52 | # Activate the specific Conda environment 53 | conda activate $CONDA_ENV_NAME 54 | 55 | print_status "installing python dependencies..." 56 | pip install -r requirements.txt 57 | 58 | print_status "creating connection in AI Studio..." 59 | python ./aistudio_create_connections.py --subscription_id $subscription_id --resource_group_name $RESOURCE_GROUP_NAME --ai_resource_name $ai_resource_name --connection_name $ai_search_connection_name --ai_search_name "$search_service_name" --ai_search_key "$aisearch_api_key" 60 | 61 | # Deactivate Conda environment 62 | conda deactivate -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/ai/mlw/main.tf: -------------------------------------------------------------------------------- 1 | 2 | resource "azapi_resource" "this" { 3 | type = "Microsoft.MachineLearningServices/workspaces@2023-02-01-preview" 4 | name = var.machine_learning_workspace_name 5 | location = var.location 6 | identity { 7 | type = "SystemAssigned" 8 | } 9 | parent_id = var.resource_group_id 10 | schema_validation_enabled = false # requiered for now 11 | body = jsonencode({ 12 | properties = { 13 | kind = "Hub" 14 | friendlyName = var.machine_learning_workspace_name 15 | keyVault = var.key_vault_id 16 | applicationInsights = var.appi_id 17 | containerRegistry = var.acr_id 18 | storageAccount = var.storage_account_id 19 | managedNetwork = { 20 | isolationMode = "Disabled" 21 | } 22 | workspaceHubConfig = { 23 | defaultWorkspaceResourceGroup = var.resource_group_id 24 | } 25 | publicNetworkAccess = "Enabled" 26 | } 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/ai/mlw/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | value = azapi_resource.this.id 3 | } 4 | 5 | output "name" { 6 | value = azapi_resource.this.name 7 | } 8 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/ai/mlw/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | source = "hashicorp/azurerm" 5 | version = "3.94.0" 6 | } 7 | azapi = { 8 | source = "azure/azapi" 9 | version = "= 1.0.0" 10 | } 11 | random = { 12 | source = "hashicorp/random" 13 | version = "3.6.0" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/ai/mlw/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_id" {} 2 | variable "location" {} 3 | variable "machine_learning_workspace_name" {} 4 | variable "appi_id" {} 5 | variable "acr_id" {} 6 | variable "storage_account_id" {} 7 | variable "key_vault_id" {} 8 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/ai/search/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_search_service" "search" { 2 | name = var.search_name 3 | location = var.location 4 | resource_group_name = var.resource_group_name 5 | sku = "basic" 6 | semantic_search_sku = "free" 7 | 8 | local_authentication_enabled = false 9 | 10 | replica_count = var.replica_count 11 | partition_count = var.partition_count 12 | hosting_mode = var.hosting_mode 13 | 14 | } 15 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/ai/search/outputs.tf: -------------------------------------------------------------------------------- 1 | output "key" { 2 | value = azurerm_search_service.search.primary_key 3 | } 4 | 5 | output "name" { 6 | value = azurerm_search_service.search.name 7 | } 8 | 9 | output "id" { 10 | value = azurerm_search_service.search.id 11 | } 12 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/ai/search/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" {} 2 | variable "location" {} 3 | variable "search_name" {} 4 | variable "search_sku" {} 5 | variable "semantic_search_sku" {} 6 | variable "replica_count" {} 7 | variable "partition_count" {} 8 | variable "hosting_mode" {} -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/containers/cr/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_container_registry" "this" { 2 | name = var.acr_name 3 | resource_group_name = var.resource_group_name 4 | location = var.location 5 | sku = "Premium" 6 | admin_enabled = false 7 | } 8 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/containers/cr/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | value = azurerm_container_registry.this.id 3 | } 4 | 5 | output "name" { 6 | value = azurerm_container_registry.this.name 7 | } 8 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/containers/cr/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" {} 2 | variable "location" {} 3 | variable "acr_name" {} 4 | 5 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/core/pe/main.tf: -------------------------------------------------------------------------------- 1 | # data resource to get the vnet 2 | data "azurerm_virtual_network" "this" { 3 | name = var.virtual_network_name 4 | resource_group_name = var.virtual_network_resource_group_name 5 | } 6 | 7 | # data resource to get the subnet 8 | data "azurerm_subnet" "this" { 9 | name = var.private_endpoints_subnet_name 10 | virtual_network_name = var.virtual_network_name 11 | resource_group_name = var.virtual_network_resource_group_name 12 | } 13 | 14 | # data resource to get the private dns zone 15 | data "azurerm_private_dns_zone" "this" { 16 | count = length(var.private_endpoints) 17 | name = var.private_endpoints[count.index].privateDnsZoneName 18 | resource_group_name = var.private_dns_zone_resource_group_name 19 | } 20 | 21 | resource "azurerm_private_endpoint" "endpoint" { 22 | count = length(var.private_endpoints) 23 | name = var.private_endpoints[count.index].privateEndpointName 24 | location = data.azurerm_virtual_network.this.location 25 | resource_group_name = var.private_endpoints[count.index].resourceGroupName 26 | subnet_id = data.azurerm_subnet.this.id 27 | 28 | private_service_connection { 29 | name = var.private_endpoints[count.index].privateEndpointName 30 | private_connection_resource_id = var.private_endpoints[count.index].resourceId 31 | is_manual_connection = false 32 | subresource_names = [var.private_endpoints[count.index].privateEndpointGroupId] 33 | } 34 | 35 | private_dns_zone_group { 36 | name = var.private_endpoints[count.index].privateEndpointGroupId 37 | private_dns_zone_ids = [data.azurerm_private_dns_zone.this[count.index].id] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/core/pe/variables.tf: -------------------------------------------------------------------------------- 1 | variable "virtual_network_name" {} 2 | variable "virtual_network_resource_group_name" {} 3 | variable "private_endpoints_subnet_name" {} 4 | variable "private_dns_zone_resource_group_name" {} 5 | variable "private_endpoints" { 6 | type = list(object({ 7 | privateEndpointName = string 8 | resourceId = string 9 | privateEndpointGroupId = string 10 | privateDnsZoneName = string 11 | resourceGroupName = string 12 | azureResourceName = string 13 | })) 14 | default = [] 15 | } 16 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/core/st/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_storage_account" "sa" { 2 | name = var.storage_account_name 3 | location = var.location 4 | resource_group_name = var.resource_group_name 5 | account_tier = "Standard" 6 | account_replication_type = "LRS" 7 | enable_https_traffic_only = true 8 | allow_nested_items_to_be_public = false 9 | } 10 | 11 | # Create containers 12 | resource "azurerm_storage_container" "container" { 13 | count = length(var.container_names) 14 | name = var.container_names[count.index] 15 | container_access_type = "private" 16 | storage_account_name = azurerm_storage_account.sa.name 17 | } 18 | 19 | # # Create the Private endpoint. This is where the Storage account gets a private IP inside the VNet.sur 20 | # resource "azurerm_private_endpoint" "endpoint" { 21 | # name = var.storage_private_endpoint_name 22 | # location = var.location 23 | # resource_group_name = var.resource_group_name 24 | # subnet_id = var.private_endpoints_subnet_id 25 | 26 | # private_service_connection { 27 | # name = "sa-peconnection" 28 | # private_connection_resource_id = azurerm_storage_account.sa.id 29 | # is_manual_connection = false 30 | # subresource_names = ["blob"] 31 | # } 32 | 33 | # private_dns_zone_group { 34 | # name = "blob-config" 35 | # private_dns_zone_ids = [azurerm_private_dns_zone.blob.id] 36 | # } 37 | # } 38 | 39 | # resource "azurerm_private_dns_zone" "blob" { 40 | # name = "privatelink.blob.core.windows.net" 41 | # resource_group_name = var.resource_group_name 42 | # } 43 | 44 | # # Link the Private Zone with the VNet 45 | # resource "azurerm_private_dns_zone_virtual_network_link" "sa" { 46 | # name = "${azurerm_private_dns_zone.blob.name}-${azurerm_private_endpoint.endpoint.name}-vnetlink" 47 | # resource_group_name = var.resource_group_name 48 | # private_dns_zone_name = azurerm_private_dns_zone.blob.name 49 | # virtual_network_id = var.virtual_network_id 50 | # } 51 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/core/st/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | value = azurerm_storage_account.sa.id 3 | } 4 | 5 | output "name" { 6 | value = azurerm_storage_account.sa.name 7 | } 8 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/core/st/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" {} 2 | variable "location" {} 3 | variable "storage_account_name" {} 4 | variable "container_names" { 5 | default = [] 6 | } 7 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/observability/appi/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_application_insights" "this" { 2 | name = var.appi_name 3 | location = var.location 4 | resource_group_name = var.resource_group_name 5 | application_type = "web" 6 | workspace_id = var.log_id 7 | } 8 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/observability/appi/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | value = azurerm_application_insights.this.id 3 | } 4 | 5 | output "appi_instrumentation_key" { 6 | value = azurerm_application_insights.this.instrumentation_key 7 | } 8 | 9 | output "appi_instrumentation_connection_string" { 10 | value = azurerm_application_insights.this.connection_string 11 | } 12 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/observability/appi/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" {} 2 | variable "location" {} 3 | variable "appi_name" {} 4 | variable "log_id" {} 5 | variable "tags" { 6 | default = {} 7 | } 8 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/observability/logs/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_log_analytics_workspace" "this" { 2 | name = var.log_name 3 | location = var.location 4 | resource_group_name = var.resource_group_name 5 | sku = "PerGB2018" 6 | retention_in_days = 30 7 | } -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/observability/logs/outputs.tf: -------------------------------------------------------------------------------- 1 | output "log_id" { 2 | value = azurerm_log_analytics_workspace.this.id 3 | } 4 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/observability/logs/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" {} 2 | variable "location" {} 3 | variable "log_name" {} 4 | variable "tags" { 5 | default = {} 6 | } 7 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/security/kv/main.tf: -------------------------------------------------------------------------------- 1 | data "azurerm_client_config" "current" {} 2 | 3 | resource "azurerm_key_vault" "kv" { 4 | name = var.keyvault_name 5 | location = var.location 6 | resource_group_name = var.resource_group_name 7 | sku_name = "standard" 8 | enabled_for_disk_encryption = false 9 | purge_protection_enabled = false 10 | enabled_for_deployment = false 11 | enabled_for_template_deployment = false 12 | enable_rbac_authorization = true 13 | soft_delete_retention_days = 7 14 | tenant_id = data.azurerm_client_config.current.tenant_id 15 | tags = var.tags 16 | } 17 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/security/kv/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | value = azurerm_key_vault.kv.id 3 | } 4 | 5 | output "name" { 6 | value = azurerm_key_vault.kv.name 7 | } 8 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/modules/security/kv/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" {} 2 | variable "location" {} 3 | variable "keyvault_name" {} 4 | variable "tags" { 5 | default = {} 6 | } 7 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | source = "hashicorp/azurerm" 5 | version = "3.94.0" 6 | } 7 | } 8 | } 9 | 10 | provider "azurerm" { 11 | features { 12 | cognitive_account { 13 | purge_soft_delete_on_destroy = true 14 | } 15 | key_vault { 16 | purge_soft_delete_on_destroy = true 17 | } 18 | } 19 | skip_provider_registration = true 20 | } 21 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" { 2 | default = "rg-standalone" 3 | } 4 | 5 | variable "location" { 6 | default = "canadaeast" 7 | } 8 | 9 | variable "storage_account_name" { 10 | default = "sastudio" 11 | } 12 | 13 | variable "keyvault_name" { 14 | default = "kv-studio" 15 | } 16 | 17 | variable "log_name" { 18 | default = "log-studio" 19 | } 20 | 21 | variable "appi_name" { 22 | default = "appi-studio" 23 | } 24 | 25 | variable "acr_name" { 26 | default = "acrstudio" 27 | } 28 | 29 | variable "search_name" { 30 | default = "search-studio" 31 | } 32 | 33 | variable "search_partition_count" { 34 | default = 1 35 | } 36 | 37 | variable "search_replica_count" { 38 | default = 1 39 | } 40 | 41 | variable "search_sku" { 42 | default = "basic" 43 | } 44 | 45 | variable "semantic_search_sku" { 46 | default = "free" 47 | } 48 | 49 | variable "hosting_mode" { 50 | default = "default" 51 | } 52 | 53 | variable "mlw_name" { 54 | default = "mlw-studio" 55 | } 56 | 57 | variable "use_random_suffix" { 58 | default = true 59 | } 60 | 61 | variable "tags" { 62 | default = {} 63 | } 64 | 65 | variable "private_dns_zone_resource_group_name" { 66 | default = "rg-standalone" 67 | } 68 | 69 | variable "virtual_network_resource_group_name" { 70 | default = "rg-standalone" 71 | } 72 | 73 | variable "virtual_network_name" { 74 | default = "vnet-ai-standalone" 75 | } 76 | 77 | variable "private_endpoints_subnet_name" { 78 | default = "private-endpoint-subnet" 79 | } 80 | 81 | variable "enable_private_endpoints" { 82 | default = false 83 | } 84 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/tests/ai-service-chat-test.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "#%pip install openai" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "#Note: The openai-python library support for Azure OpenAI is in preview.\n", 19 | " #Note: This code sample requires OpenAI Python library version 0.28.1 or lower.\n", 20 | "import os\n", 21 | "from openai import AzureOpenAI\n", 22 | "from azure.identity import DefaultAzureCredential, get_bearer_token_provider\n", 23 | "\n", 24 | "api_version = \"2023-12-01-preview\"\n", 25 | "endpoint = \"https://aianildwa4rda.openai.azure.com/\"\n", 26 | "\n", 27 | "# Assign Cognitive Services OpenAI Contributor to current logged in user\n", 28 | "\n", 29 | "token_provider = get_bearer_token_provider(\n", 30 | " DefaultAzureCredential(),\n", 31 | " \"https://cognitiveservices.azure.com/.default\"\n", 32 | " )\n", 33 | "\n", 34 | "openai_client = AzureOpenAI(\n", 35 | " api_version=api_version,\n", 36 | " azure_endpoint=endpoint,\n", 37 | " azure_ad_token_provider=token_provider\n", 38 | ")\n", 39 | "\n", 40 | "message_text = [{\"role\":\"system\",\"content\":\"You are an AI assistant that helps people find information.\"},\n", 41 | " {\"role\":\"user\",\"content\":\"Tell me a story in 50 words.\"}]\n", 42 | "\n", 43 | "completion = openai_client.chat.completions.create(\n", 44 | " model=\"aianildwa4rda-gpt-4o\",\n", 45 | " messages = message_text,\n", 46 | " temperature=0.7,\n", 47 | " max_tokens=800,\n", 48 | " top_p=0.95,\n", 49 | " frequency_penalty=0,\n", 50 | " presence_penalty=0,\n", 51 | " stop=None\n", 52 | ")\n", 53 | "\n", 54 | "print(completion.choices[0].message.content)" 55 | ] 56 | } 57 | ], 58 | "metadata": { 59 | "kernelspec": { 60 | "display_name": "aoai-landing-zone", 61 | "language": "python", 62 | "name": "python3" 63 | }, 64 | "language_info": { 65 | "codemirror_mode": { 66 | "name": "ipython", 67 | "version": 3 68 | }, 69 | "file_extension": ".py", 70 | "mimetype": "text/x-python", 71 | "name": "python", 72 | "nbconvert_exporter": "python", 73 | "pygments_lexer": "ipython3", 74 | "version": "3.10.13" 75 | } 76 | }, 77 | "nbformat": 4, 78 | "nbformat_minor": 2 79 | } 80 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/tests/pftest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | print_status() { 4 | echo -e "\e[32m$1\e[0m" 5 | } 6 | 7 | print_warning() { 8 | echo -e "\e[33m$1\e[0m" 9 | } 10 | 11 | RESOURCE_GROUP_NAME=$1 12 | 13 | #CONDA_ENV_NAME="aoai-landingzone" 14 | #source ~/miniconda3/etc/profile.d/conda.sh 15 | #conda activate $CONDA_ENV_NAME 16 | 17 | 18 | while [ -z "${RESOURCE_GROUP_NAME}" ] 19 | do 20 | print_warning "Please provide resource group name:" 21 | read RESOURCE_GROUP_NAME 22 | done 23 | 24 | RG_EXISTS=$(az group exists -g $RESOURCE_GROUP_NAME | jq -r '.') 25 | 26 | if [ $RG_EXISTS = "false" ] 27 | then 28 | print_warning "Resource group $RESOURCE_GROUP_NAME does not exist. Please run create_ai_studio.sh script first." 29 | exit 1 30 | fi 31 | 32 | ai_service_name=$(az resource list -g $RESOURCE_GROUP_NAME --query "[?type=='Microsoft.CognitiveServices/accounts']" | jq -r .[].name) 33 | ai_service_key=$(az cognitiveservices account keys list -g $RESOURCE_GROUP_NAME -n $ai_service_name | jq -r .key1) 34 | api_base=$(az cognitiveservices account show --name $ai_service_name -g $RESOURCE_GROUP_NAME | jq -r '.properties.endpoints["OpenAI Language Model Instance API"]') 35 | ai_service_deployment_name=$(az cognitiveservices account deployment list --name $ai_service_name -g $RESOURCE_GROUP_NAME | jq -r .[0].name) 36 | 37 | 38 | sed -i "/deployment_name:/s|.*| deployment_name: $ai_service_deployment_name |" ./pftest_chatbot/flow.dag.yaml 39 | 40 | 41 | #pf connection create --file ./pftest_chatbot/azure_openai.yaml --set api_key=$ai_service_key api_base=$api_base --name open_ai_connection 42 | pf connection create --file ./pftest_chatbot/azure_ai_services.yaml --set api_base=$api_base --name open_ai_connection 43 | pf flow test --flow ./pftest_chatbot --interactive 44 | 45 | 46 | conda deactivate -------------------------------------------------------------------------------- /foundation/aistudio-infra/tests/pftest_chatbot/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | __pycache__/ 3 | .promptflow/* 4 | !.promptflow/flow.tools.json 5 | .runs/ 6 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/tests/pftest_chatbot/.promptflow/flow.tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "package": {}, 3 | "code": { 4 | "chat.jinja2": { 5 | "type": "llm", 6 | "inputs": { 7 | "question": { 8 | "type": [ 9 | "string" 10 | ] 11 | }, 12 | "chat_history": { 13 | "type": [ 14 | "string" 15 | ] 16 | } 17 | }, 18 | "description": "Chat with Chatbot" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/tests/pftest_chatbot/azure_ai_services.yaml: -------------------------------------------------------------------------------- 1 | $schema: https://azuremlschemas.azureedge.net/promptflow/latest/AzureOpenAIConnection.schema.json 2 | name: my_azure_open_ai_connection 3 | type: azure_open_ai # snake case 4 | api_base: "aoai-api-endpoint" 5 | api_type: "azure" 6 | api_version: "2023-07-01-preview" 7 | auth_mode: meid_token 8 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/tests/pftest_chatbot/azure_openai.yaml: -------------------------------------------------------------------------------- 1 | $schema: https://azuremlschemas.azureedge.net/promptflow/latest/AzureOpenAIConnection.schema.json 2 | name: open_ai_connection 3 | type: azure_open_ai 4 | #api_key: "" 5 | api_base: "" 6 | api_type: "azure" 7 | auth_mode: "meid_token" -------------------------------------------------------------------------------- /foundation/aistudio-infra/tests/pftest_chatbot/chat.jinja2: -------------------------------------------------------------------------------- 1 | system: 2 | You are a helpful assistant. 3 | 4 | {% for item in chat_history %} 5 | user: 6 | {{item.inputs.question}} 7 | assistant: 8 | {{item.outputs.answer}} 9 | {% endfor %} 10 | 11 | user: 12 | {{question}} 13 | -------------------------------------------------------------------------------- /foundation/aistudio-infra/tests/pftest_chatbot/flow.dag.yaml: -------------------------------------------------------------------------------- 1 | $schema: https://azuremlschemas.azureedge.net/promptflow/latest/Flow.schema.json 2 | inputs: 3 | chat_history: 4 | type: list 5 | is_chat_history: true 6 | default: [] 7 | question: 8 | type: string 9 | is_chat_input: true 10 | outputs: 11 | answer: 12 | type: string 13 | reference: ${chat.output} 14 | is_chat_output: true 15 | nodes: 16 | - name: chat 17 | type: llm 18 | source: 19 | type: code 20 | path: chat.jinja2 21 | inputs: 22 | deployment_name: aianildwa4rda-gpt-4o 23 | max_tokens: '256' 24 | temperature: '0.7' 25 | chat_history: ${inputs.chat_history} 26 | question: ${inputs.question} 27 | api: chat 28 | connection: open_ai_connection 29 | environment: 30 | python_requirements_txt: requirements.txt -------------------------------------------------------------------------------- /foundation/aistudio-infra/tests/pftest_chatbot/openai.yaml: -------------------------------------------------------------------------------- 1 | $schema: https://azuremlschemas.azureedge.net/promptflow/latest/OpenAIConnection.schema.json 2 | name: open_ai_connection 3 | type: open_ai 4 | api_key: "" -------------------------------------------------------------------------------- /foundation/aistudio-infra/tests/pftest_chatbot/pip2.txt: -------------------------------------------------------------------------------- 1 | aniso8601==9.0.1 2 | annotated-types==0.7.0 3 | anyio==4.4.0 4 | argcomplete==3.5.0 5 | attrs==24.2.0 6 | azure-core==1.30.2 7 | azure-identity==1.17.1 8 | azure-monitor-opentelemetry-exporter==1.0.0b28 9 | backports.tarfile==1.2.0 10 | blinker==1.8.2 11 | certifi==2024.7.4 12 | cffi==1.17.0 13 | charset-normalizer==3.3.2 14 | click==8.1.7 15 | colorama==0.4.6 16 | cryptography==43.0.0 17 | Deprecated==1.2.14 18 | distro==1.9.0 19 | docstring_parser==0.16 20 | exceptiongroup==1.2.2 21 | fastapi==0.112.1 22 | filelock==3.15.4 23 | filetype==1.2.0 24 | fixedint==0.1.6 25 | Flask==3.0.3 26 | Flask-Cors==4.0.1 27 | flask-restx==1.3.0 28 | gitdb==4.0.11 29 | GitPython==3.1.43 30 | google_search_results==2.4.1 31 | googleapis-common-protos==1.63.2 32 | greenlet==3.0.3 33 | h11==0.14.0 34 | httpcore==1.0.5 35 | httpx==0.27.0 36 | idna==3.8 37 | importlib_metadata==8.0.0 38 | importlib_resources==6.4.4 39 | isodate==0.6.1 40 | itsdangerous==2.2.0 41 | jaraco.classes==3.4.0 42 | jaraco.context==6.0.1 43 | jeepney==0.8.0 44 | Jinja2==3.1.4 45 | jiter==0.5.0 46 | jsonschema==4.23.0 47 | jsonschema-specifications==2023.12.1 48 | keyring==24.3.1 49 | keyrings.alt==5.0.2 50 | MarkupSafe==2.1.5 51 | marshmallow==3.22.0 52 | more-itertools==10.4.0 53 | msal==1.30.0 54 | msal-extensions==1.2.0 55 | msrest==0.7.1 56 | numpy==2.1.0 57 | oauthlib==3.2.2 58 | openai==1.42.0 59 | opentelemetry-api==1.26.0 60 | opentelemetry-exporter-otlp-proto-common==1.26.0 61 | opentelemetry-exporter-otlp-proto-http==1.26.0 62 | opentelemetry-proto==1.26.0 63 | opentelemetry-sdk==1.26.0 64 | opentelemetry-semantic-conventions==0.47b0 65 | packaging==24.1 66 | pandas==2.2.2 67 | pillow==10.4.0 68 | portalocker==2.10.1 69 | promptflow==1.15.0 70 | promptflow-core==1.15.0 71 | promptflow-devkit==1.15.0 72 | promptflow-tools==1.4.0 73 | promptflow-tracing==1.15.0 74 | protobuf==4.25.4 75 | psutil==5.9.8 76 | pycparser==2.22 77 | pydantic==2.8.2 78 | pydantic_core==2.20.1 79 | pydash==7.0.7 80 | PyJWT==2.9.0 81 | python-dateutil==2.9.0.post0 82 | python-dotenv==1.0.1 83 | pytz==2024.1 84 | referencing==0.35.1 85 | regex==2024.7.24 86 | requests==2.32.3 87 | requests-oauthlib==2.0.0 88 | rpds-py==0.20.0 89 | ruamel.yaml==0.18.6 90 | ruamel.yaml.clib==0.2.8 91 | SecretStorage==3.3.3 92 | six==1.16.0 93 | smmap==5.0.1 94 | sniffio==1.3.1 95 | SQLAlchemy==2.0.32 96 | starlette==0.38.2 97 | strictyaml==1.7.3 98 | tabulate==0.9.0 99 | tiktoken==0.7.0 100 | tqdm==4.66.5 101 | typing_extensions==4.12.2 102 | tzdata==2024.1 103 | urllib3==2.2.2 104 | waitress==2.1.2 105 | Werkzeug==3.0.4 106 | wrapt==1.16.0 107 | zipp==3.20.0 -------------------------------------------------------------------------------- /foundation/aistudio-infra/tests/pftest_chatbot/requirements.txt: -------------------------------------------------------------------------------- 1 | promptflow 2 | promptflow-tools 3 | keyrings.alt 4 | azure.identity -------------------------------------------------------------------------------- /foundation/aoai-infra/bicep/aoai.bicep: -------------------------------------------------------------------------------- 1 | @description('Location for all resources.') 2 | @allowed([ 3 | 'australiaeast' 4 | 'canadaeast' 5 | 'eastus' 6 | 'eastus2' 7 | 'francecentral' 8 | 'japaneast' 9 | 'northcentralus' 10 | 'southcentralus' 11 | 'swedencentral' 12 | 'switzerlandnorth' 13 | 'uksouth' 14 | 'westeurope' 15 | 'westus' 16 | ]) 17 | param location string = 'westus' 18 | 19 | 20 | @description('That name is the name of our application. It has to be unique.Type a name followed by your resource group name. (-)') 21 | param aoaiServiceName string 22 | 23 | 24 | 25 | param deployments array = [] 26 | 27 | 28 | var sku = 'S0' 29 | 30 | 31 | 32 | resource cognitiveService 'Microsoft.CognitiveServices/accounts@2023-05-01' = { 33 | name: aoaiServiceName 34 | location: location 35 | sku: { 36 | name: sku 37 | } 38 | kind: 'OpenAI' 39 | properties: { 40 | publicNetworkAccess: 'Disabled' 41 | customSubDomainName: aoaiServiceName 42 | } 43 | } 44 | 45 | 46 | 47 | @batchSize(1) 48 | resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { 49 | name: deployment.name 50 | parent: cognitiveService 51 | properties: { 52 | model: deployment.model 53 | raiPolicyName: deployment.raiPolicyName 54 | } 55 | sku: deployment.sku 56 | }] 57 | 58 | 59 | -------------------------------------------------------------------------------- /foundation/aoai-infra/bicep/aoairole.bicep: -------------------------------------------------------------------------------- 1 | 2 | @description('Azure OpenAI Resource Name.') 3 | param aoaiServiceName string 4 | 5 | var principalIds = loadJsonContent('./userPrincipalIds.json') 6 | 7 | //'Cognitive Services Contributor': resourceId('Microsoft.Authorization/roleAssignments', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') 8 | //'Cognitive Services OpenAI Contributor': resourceId('Microsoft.Authorization/roleAssignments', 'a001fd3d-188f-4b5d-821b-7da978bf7442') 9 | //'Cognitive Services OpenAI User': resourceId('Microsoft.Authorization/roleAssignments', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd') 10 | //'Cognitive Services User': resourceId('Microsoft.Authorization/roleAssignments', 'a97b65f3-24c7-4388-baec-2e87135dc908') 11 | 12 | 13 | //var roleAssignmentName= guid(principalId, contributorRoleDefinitionID, cognitiveService.id) 14 | resource cognitiveService 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { 15 | name: aoaiServiceName 16 | } 17 | 18 | resource contributorRoleID 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds.contributorPrincipalIds: { 19 | name: guid(principalId, '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68', cognitiveService.id) 20 | scope: cognitiveService 21 | properties: { 22 | roleDefinitionId: resourceId('Microsoft.Authorization/roleAssignments', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') 23 | principalId: principalId 24 | } 25 | }] 26 | 27 | 28 | resource userRoleID 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds.userPrincipalIds: { 29 | name: guid(principalId, 'a97b65f3-24c7-4388-baec-2e87135dc908', cognitiveService.id) 30 | scope: cognitiveService 31 | properties: { 32 | roleDefinitionId: resourceId('Microsoft.Authorization/roleAssignments', 'a97b65f3-24c7-4388-baec-2e87135dc908') 33 | principalId: principalId 34 | } 35 | }] 36 | 37 | -------------------------------------------------------------------------------- /foundation/aoai-infra/bicep/main.bicepparam: -------------------------------------------------------------------------------- 1 | using './main.bicep' 2 | 3 | param location = 'northcentralus' 4 | param aoaiServiceName = 'aoai-7uj23hng7h22c-northcentralus' 5 | param privateEndpointName = 'aoai-7uj23hng7h22c-northcentralus-pe' 6 | param vnetRgName = 'vnet' 7 | param privateDNSZoneRgName = 'vnet' 8 | param vnetName = 'vnet-westus' 9 | param vnetLocation = 'westus' 10 | param subnetName = 'default' 11 | param aoai_deployments = ['anildwa-test1-gpt-35-turbo', 'anildwa-test1-gpt-4'] 12 | param capacity = [30,35] 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /foundation/aoai-infra/bicep/main.bicepparam.json: -------------------------------------------------------------------------------- 1 | { 2 | "location": "northcentralus", 3 | "aoaiServiceName": "aoai-7uj23hng7h22c-northcentralus", 4 | "privateEndpointName": "aoai-7uj23hng7h22c-northcentralus-pe", 5 | "vnetRgName": "vnet", 6 | "privateDNSZoneRgName": "vnet", 7 | "vnetName": "vnet-westus", 8 | "vnetLocation": "westus", 9 | "subnetName": "default" 10 | } -------------------------------------------------------------------------------- /foundation/aoai-infra/bicep/privateendpoint.bicep: -------------------------------------------------------------------------------- 1 | @description('Azure OpenAI Resource Name.') 2 | param aoaiServiceName string 3 | 4 | @description('Private endpoint for a Azure OpenAI Resource.') 5 | param privateEndpointName string 6 | 7 | @description('Vnet resource group name.') 8 | param vnetRgName string 9 | 10 | @description('Private DNS Zone resource group name.') 11 | param privateDNSZoneRgName string 12 | 13 | @description('Vnet location.') 14 | param vnetLocation string 15 | 16 | @description('Vnet name.') 17 | param vnetName string 18 | 19 | @description('Subnet name where the private endpoint should be provisioned.') 20 | param subnetName string 21 | 22 | 23 | var privateDnsZoneName = 'privatelink.openai.azure.com' 24 | 25 | resource cognitiveService 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { 26 | name: aoaiServiceName 27 | } 28 | 29 | 30 | resource vnet 'Microsoft.Network/virtualNetworks@2023-05-01' existing = { 31 | name: vnetName 32 | scope: resourceGroup(vnetRgName) 33 | } 34 | 35 | resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-05-01' existing = { 36 | parent: vnet 37 | name: subnetName 38 | } 39 | 40 | resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-05-01' = { 41 | name: privateEndpointName 42 | location: vnetLocation 43 | properties: { 44 | subnet: { 45 | id: subnet.id 46 | } 47 | privateLinkServiceConnections: [ 48 | { 49 | name: privateEndpointName 50 | properties: { 51 | privateLinkServiceId: cognitiveService.id 52 | groupIds: [ 53 | 'account' 54 | ] 55 | } 56 | } 57 | ] 58 | } 59 | } 60 | 61 | 62 | resource pvtEndpointDnsGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-05-01' = { 63 | parent: privateEndpoint 64 | name: 'dnsgroup' 65 | properties: { 66 | privateDnsZoneConfigs: [ 67 | { 68 | name: '${aoaiServiceName}-config' 69 | properties: { 70 | privateDnsZoneId: resourceId(privateDNSZoneRgName, 'Microsoft.Network/privateDnsZones', privateDnsZoneName) 71 | } 72 | } 73 | ] 74 | } 75 | 76 | } 77 | 78 | -------------------------------------------------------------------------------- /foundation/aoai-infra/bicep/userPrincipalIds.json: -------------------------------------------------------------------------------- 1 | { "contributorPrincipalIds" :["ffb6c4cf-8a03-481c-bf6a-f5edbebffc25"], 2 | "userPrincipalIds" :["ffb6c4cf-8a03-481c-bf6a-f5edbebffc25"]} -------------------------------------------------------------------------------- /foundation/aoai-infra/terraform/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/azurerm" { 5 | version = "3.87.0" 6 | constraints = "3.87.0" 7 | hashes = [ 8 | "h1:SqFtup3wvbozutkXVF78LylpKL4nGED1cbk2IbLzhAQ=", 9 | "zh:1547ed020fa6ca25d940b28601442c7e4495fdea9fb1ead7affb867383f5f40b", 10 | "zh:325e6d636b5ab09a24837194647617c9fabd42f0fb2c7e18ae8d2a8b2d890a55", 11 | "zh:3abb0074de1dc3b723f8c209354ba93e717ba52003847484b19369e8f54735f4", 12 | "zh:52d2b1700108d5093113a986aa10757834e48083c1358a2c7d8d0360e2689390", 13 | "zh:5fe377d5cc80e26766ff411dbcb227728709fe34b14ad106c9e374df653086a4", 14 | "zh:747fe80de4fb88b17cac93ff05d62909d3563325c8ed5a461641b48579c328f8", 15 | "zh:b40142e4041b7f000ab2dda58309755395a4018d5d00218f6a601f737389865a", 16 | "zh:bca622818c221cec81d636879e376c15696a8d091703298195d728b3c1eae7db", 17 | "zh:bfaecd203137ff9eb3228b1cbd3191e1d84d7c019855eb5f3071bbf6eb060a51", 18 | "zh:d197f04b54f2be07f827ced220954d723039c84793a4ce91894b622982c25811", 19 | "zh:e831601ea1f67c5e745946ed3ac0cac772ed8e95ca7d7314d3f0ed631e6eefb1", 20 | "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /foundation/aoai-infra/terraform/main.tf: -------------------------------------------------------------------------------- 1 | data "azurerm_subscription" "current" {} 2 | 3 | data "azurerm_resource_group" "rg" { 4 | name = var.resource_group_name 5 | } 6 | 7 | module "openai" { 8 | source = "./modules/ai/cognitive" 9 | resource_group_name = data.azurerm_resource_group.rg.name 10 | location = var.location 11 | cognitive_name = var.openai_name 12 | tags = var.tags 13 | cognitive_private_endpoint_name = var.openai_private_endpoint_name 14 | deployments = var.openai_deployments 15 | virtual_network_name = var.virtual_network_name 16 | virtual_network_resource_group_name = var.virtual_network_resource_group_name 17 | private_endpoints_subnet_name = var.private_endpoints_subnet_name 18 | private_dns_zone_resource_group_name = var.private_dns_zone_resource_group_name 19 | } 20 | 21 | module "rbac" { 22 | source = "./modules/security/rbac" 23 | contributor_principal_ids = var.contributor_principal_ids 24 | user_principal_ids = var.user_principal_ids 25 | cognitive_service_id = module.openai.id 26 | } 27 | -------------------------------------------------------------------------------- /foundation/aoai-infra/terraform/modules/ai/cognitive/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_cognitive_account" "this" { 2 | name = var.cognitive_name 3 | kind = var.cognitive_kind 4 | sku_name = var.cognitive_sku 5 | location = var.location 6 | resource_group_name = var.resource_group_name 7 | public_network_access_enabled = false 8 | custom_subdomain_name = lower(var.cognitive_name) 9 | } 10 | 11 | # data resource to get the vnet 12 | data "azurerm_virtual_network" "this" { 13 | name = var.virtual_network_name 14 | resource_group_name = var.virtual_network_resource_group_name 15 | } 16 | 17 | # data resource to get the subnet 18 | data "azurerm_subnet" "this" { 19 | name = var.private_endpoints_subnet_name 20 | virtual_network_name = var.virtual_network_name 21 | resource_group_name = var.virtual_network_resource_group_name 22 | } 23 | 24 | # data resurce to get the private dns zone 25 | data "azurerm_private_dns_zone" "this" { 26 | name = var.private_dns_zone_name 27 | resource_group_name = var.private_dns_zone_resource_group_name 28 | } 29 | 30 | resource "azurerm_private_endpoint" "endpoint" { 31 | name = var.cognitive_private_endpoint_name 32 | location = data.azurerm_virtual_network.this.location 33 | resource_group_name = var.resource_group_name 34 | subnet_id = data.azurerm_subnet.this.id 35 | tags = var.tags 36 | 37 | private_service_connection { 38 | name = var.cognitive_private_endpoint_name 39 | private_connection_resource_id = azurerm_cognitive_account.this.id 40 | is_manual_connection = false 41 | subresource_names = ["account"] 42 | } 43 | 44 | private_dns_zone_group { 45 | name = "dnsgroup" 46 | private_dns_zone_ids = [data.azurerm_private_dns_zone.this.id] 47 | } 48 | } 49 | 50 | resource "azurerm_cognitive_deployment" "deployment" { 51 | for_each = var.deployments 52 | name = each.value.model.name 53 | cognitive_account_id = azurerm_cognitive_account.this.id 54 | rai_policy_name = each.value.model.rai_policy_name 55 | model { 56 | format = each.value.model.format 57 | name = each.value.model.name 58 | version = each.value.model.version 59 | } 60 | 61 | scale { 62 | type = each.value.sku.name 63 | capacity = each.value.sku.capacity 64 | } 65 | 66 | depends_on = [ 67 | azurerm_cognitive_account.this, 68 | azurerm_private_endpoint.endpoint 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /foundation/aoai-infra/terraform/modules/ai/cognitive/outputs.tf: -------------------------------------------------------------------------------- 1 | output "key" { 2 | value = azurerm_cognitive_account.this.primary_access_key 3 | sensitive = true 4 | } 5 | 6 | output "name" { 7 | value = azurerm_cognitive_account.this.name 8 | } 9 | 10 | output "id" { 11 | value = azurerm_cognitive_account.this.id 12 | } 13 | -------------------------------------------------------------------------------- /foundation/aoai-infra/terraform/modules/ai/cognitive/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" {} 2 | variable "location" {} 3 | variable "cognitive_name" {} 4 | variable "cognitive_kind" { 5 | default = "OpenAI" 6 | } 7 | variable "cognitive_sku" { 8 | default = "S0" 9 | } 10 | variable "deployments" { 11 | default = {} 12 | } 13 | variable "cognitive_private_endpoint_name" {} 14 | variable "virtual_network_name" {} 15 | variable "virtual_network_resource_group_name" {} 16 | variable "private_endpoints_subnet_name" {} 17 | variable "private_dns_zone_resource_group_name" {} 18 | variable "private_dns_zone_name" { 19 | default = "privatelink.openai.azure.com" 20 | } 21 | variable "tags" { 22 | default = {} 23 | } 24 | -------------------------------------------------------------------------------- /foundation/aoai-infra/terraform/modules/security/rbac/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_role_assignment" "contributor" { 2 | count = length(var.contributor_principal_ids) 3 | scope = var.cognitive_service_id 4 | role_definition_name = "Cognitive Services Contributor" 5 | principal_id = var.contributor_principal_ids[count.index] 6 | } 7 | 8 | resource "azurerm_role_assignment" "user" { 9 | count = length(var.user_principal_ids) 10 | scope = var.cognitive_service_id 11 | role_definition_name = "Cognitive Services User" 12 | principal_id = var.contributor_principal_ids[count.index] 13 | } 14 | -------------------------------------------------------------------------------- /foundation/aoai-infra/terraform/modules/security/rbac/variables.tf: -------------------------------------------------------------------------------- 1 | variable "contributor_principal_ids" { 2 | default = [] 3 | } 4 | variable "user_principal_ids" { 5 | default = [] 6 | } 7 | variable "cognitive_service_id" {} 8 | -------------------------------------------------------------------------------- /foundation/aoai-infra/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | source = "hashicorp/azurerm" 5 | version = "3.87.0" 6 | } 7 | } 8 | } 9 | 10 | provider "azurerm" { 11 | features { 12 | cognitive_account { 13 | purge_soft_delete_on_destroy = true 14 | } 15 | } 16 | skip_provider_registration = true 17 | } 18 | -------------------------------------------------------------------------------- /foundation/aoai-infra/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" { 2 | default = "rg-standalone" 3 | } 4 | 5 | variable "location" { 6 | default = "canadaeast" 7 | } 8 | 9 | variable "openai_name" { 10 | default = "aoai-7uj23hng7h22c" 11 | } 12 | 13 | variable "openai_private_endpoint_name" { 14 | default = "aoai-7uj23hng7h22c-pe" 15 | } 16 | 17 | variable "virtual_network_name" { 18 | default = "vnet-ai-standalone" 19 | } 20 | 21 | variable "virtual_network_resource_group_name" { 22 | default = "rg-standalone" 23 | } 24 | 25 | variable "private_endpoints_subnet_name" { 26 | default = "private-endpoint-subnet" 27 | } 28 | 29 | variable "private_dns_zone_resource_group_name" { 30 | default = "rg-standalone" 31 | } 32 | 33 | variable "openai_deployments" { 34 | default = { 35 | gpt4 = { 36 | name = "gpt-4" 37 | model = { 38 | format = "OpenAI" 39 | name = "gpt-4" 40 | version = "1106-Preview" 41 | rai_policy_name = "Microsoft.Default" 42 | } 43 | sku = { 44 | name = "Standard" 45 | capacity = 5 46 | } 47 | } 48 | embeddings = { 49 | name = "text-embedding-ada-002" 50 | model = { 51 | format = "OpenAI" 52 | name = "text-embedding-ada-002" 53 | version = "2" 54 | rai_policy_name = "Microsoft.Default" 55 | } 56 | sku = { 57 | name = "Standard" 58 | capacity = 5 59 | } 60 | } 61 | } 62 | } 63 | 64 | variable "contributor_principal_ids" { 65 | default = [] 66 | } 67 | 68 | variable "user_principal_ids" { 69 | default = [] 70 | } 71 | 72 | variable "tags" { 73 | default = {} 74 | } 75 | -------------------------------------------------------------------------------- /foundation/aoai-infra/test.ps1: -------------------------------------------------------------------------------- 1 | # PowerShell script 2 | 3 | # Read Azure OpenAI Resource Name 4 | $aoaiServiceName = $args[0] 5 | if ($aoaiServiceName -eq $null -or $aoaiServiceName -eq "") { 6 | Write-Host "Existing Azure OpenAI Resource Name:" 7 | $aoaiServiceName = Read-Host 8 | } 9 | 10 | # Read Azure OpenAI Model Deployment Name 11 | $deploymentName = $args[1] 12 | if ($deploymentName -eq $null -or $deploymentName -eq "") { 13 | Write-Host "Azure OpenAI Model Deployment Name:" 14 | $deploymentName = Read-Host 15 | } 16 | 17 | # Get Tenant ID 18 | $tenantId = (az account show | ConvertFrom-Json).tenantId 19 | 20 | # Get Access Token 21 | $accessToken = (az account get-access-token --resource "https://cognitiveservices.azure.com/" --tenant $tenantId | ConvertFrom-Json).accessToken 22 | 23 | # Prepare the header 24 | $headers = @{ 25 | "Content-Type" = "application/json" 26 | "Authorization" = "Bearer $accessToken" 27 | "api-type" = "azure_ad" 28 | } 29 | 30 | # Prepare the body 31 | $body = @{ 32 | max_tokens = 70 33 | messages = @( 34 | @{ 35 | role = "system" 36 | content = "You are a helpful assistant. Generate full sentence." 37 | }, 38 | @{ 39 | role = "user" 40 | content = "Tell me something about Azure OpenAI" 41 | } 42 | ) 43 | } | ConvertTo-Json 44 | 45 | # Invoke the REST API 46 | $uri = "https://$aoaiServiceName.openai.azure.com/openai/deployments/$deploymentName/chat/completions?api-version=2023-05-15" 47 | if (-not [string]::IsNullOrEmpty($aoaiServiceName) -and -not [string]::IsNullOrEmpty($deploymentName)) { 48 | # Execute the Invoke-RestMethod command and store the result 49 | $response = Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -Body $body 50 | $output = $response.choices[0].message.content 51 | Write-Output $output 52 | 53 | } else { 54 | Write-Host "Service Name or Deployment Name is missing." 55 | } 56 | -------------------------------------------------------------------------------- /foundation/aoai-infra/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | aoaiServiceName=$1 5 | if [ -z "${aoaiServiceName}" ]; then 6 | echo "Existing Azure OpenAI Resource Name:" 7 | read aoaiServiceName 8 | fi 9 | 10 | 11 | deploymentName=$2 12 | if [ -z "${deploymentName}" ]; then 13 | echo "Azure OpenAI Model Deployment Name:" 14 | read deploymentName 15 | fi 16 | 17 | #az ad user show --id pbiuser@anildwa1.onmicrosoft.com 18 | resource="https://cognitiveservices.azure.com/" 19 | tenantId=$(az account show | jq -r .tenantId) 20 | accessToken=$(az account get-access-token --resource $resource --tenant $tenantId | jq -r .accessToken) 21 | 22 | curl https://$aoaiServiceName.openai.azure.com/openai/deployments/$deploymentName/chat/completions?api-version=2023-05-15\ 23 | -H "Content-Type: application/json" \ 24 | -H "Authorization: Bearer $accessToken" \ 25 | -H "api-type: azure_ad" \ 26 | -d '{"max_tokens": 70, "messages":[{"role": "system", "content": "You are a helpful assistant. Generate full sentence."},{"role": "user", "content": "Tell me something about Azure OpenAI"}]}' 27 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/assets/apim_endpoint_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-openai-landing-zone/e5a785144593b00ac9fba58322a1651ff5d9b06e/foundation/apimv2-infra/assets/apim_endpoint_1.png -------------------------------------------------------------------------------- /foundation/apimv2-infra/assets/apim_key_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-openai-landing-zone/e5a785144593b00ac9fba58322a1651ff5d9b06e/foundation/apimv2-infra/assets/apim_key_1.png -------------------------------------------------------------------------------- /foundation/apimv2-infra/assets/endpoint_and_key_mapping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/azure-openai-landing-zone/e5a785144593b00ac9fba58322a1651ff5d9b06e/foundation/apimv2-infra/assets/endpoint_and_key_mapping.png -------------------------------------------------------------------------------- /foundation/apimv2-infra/bicep/modules/apimService.bicep: -------------------------------------------------------------------------------- 1 | //**************************************************************************************** 2 | // Parameters 3 | //**************************************************************************************** 4 | 5 | @description('Name of the API Management instance. Must be globally unique.') 6 | param apiManagementName string 7 | 8 | @description('Location for the resource.') 9 | param location string 10 | 11 | //**************************************************************************************** 12 | // Variables 13 | //**************************************************************************************** 14 | 15 | var publisherEmail = 'admin@contoso.com' 16 | var publisherName = 'ContosoAdmin' 17 | 18 | //**************************************************************************************** 19 | // Resources 20 | //**************************************************************************************** 21 | 22 | resource apiManagement 'Microsoft.ApiManagement/service@2023-05-01-preview' = { 23 | name: apiManagementName 24 | location: location 25 | sku: { 26 | name: 'StandardV2' 27 | capacity: 1 28 | } 29 | identity: { 30 | type: 'SystemAssigned' 31 | } 32 | properties: { 33 | publisherEmail: publisherEmail 34 | publisherName: publisherName 35 | apiVersionConstraint: {} 36 | developerPortalStatus: 'Disabled' 37 | } 38 | } 39 | 40 | //**************************************************************************************** 41 | // Outputs 42 | //**************************************************************************************** 43 | 44 | output managedIdentityPrincipalID string = apiManagement.identity.principalId 45 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/bicep/modules/appinsights.bicep: -------------------------------------------------------------------------------- 1 | @description('Name of the Application Insights to be created.') 2 | param applicationInsightsName string 3 | 4 | @description('Location for the resource.') 5 | param location string 6 | 7 | @description('Resource ID of the Log Analytics Workspace that Application Insights will be linked to.') 8 | param workspaceResourceId string 9 | 10 | resource applicationInsights 'microsoft.insights/components@2020-02-02' = { 11 | name: applicationInsightsName 12 | location: location 13 | kind: 'web' 14 | properties: { 15 | Application_Type: 'web' 16 | Flow_Type: 'Redfield' 17 | Request_Source: 'IbizaAIExtension' 18 | RetentionInDays: 90 19 | WorkspaceResourceId: workspaceResourceId 20 | IngestionMode: 'LogAnalytics' 21 | publicNetworkAccessForIngestion: 'Enabled' 22 | publicNetworkAccessForQuery: 'Enabled' 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/bicep/modules/eventhub.bicep: -------------------------------------------------------------------------------- 1 | @description('Name of the event hub namespace to be created. Must be globally unique.') 2 | param eventHubNamespaceName string 3 | 4 | @description('Name of the event hub to be created.') 5 | param eventHubName string 6 | 7 | @description('Location for the resource.') 8 | param location string 9 | 10 | resource eventHubNamespace 'Microsoft.EventHub/namespaces@2023-01-01-preview' = { 11 | name: eventHubNamespaceName 12 | location: location 13 | sku: { 14 | name: 'Basic' 15 | tier: 'Basic' 16 | capacity: 1 17 | } 18 | properties: { 19 | minimumTlsVersion: '1.2' 20 | publicNetworkAccess: 'Enabled' 21 | disableLocalAuth: false 22 | zoneRedundant: true 23 | isAutoInflateEnabled: false 24 | } 25 | } 26 | 27 | resource eventHubForAPIM 'Microsoft.EventHub/namespaces/eventhubs@2023-01-01-preview' = { 28 | parent: eventHubNamespace 29 | name: eventHubName 30 | properties: { 31 | retentionDescription: { 32 | cleanupPolicy: 'Delete' 33 | retentionTimeInHours: 1 34 | } 35 | messageRetentionInDays: 1 36 | partitionCount: 2 37 | status: 'Active' 38 | } 39 | } 40 | 41 | resource eventHubForAPIMDiagnosticSettings 'Microsoft.EventHub/namespaces/eventhubs@2023-01-01-preview' = { 42 | parent: eventHubNamespace 43 | name: 'insights-metrics-pt1m' 44 | properties: { 45 | retentionDescription: { 46 | cleanupPolicy: 'Delete' 47 | retentionTimeInHours: 1 48 | } 49 | messageRetentionInDays: 1 50 | partitionCount: 2 51 | status: 'Active' 52 | } 53 | } 54 | 55 | resource eventHubForAPIMAuthorizationRule 'Microsoft.EventHub/namespaces/eventhubs/authorizationRules@2022-10-01-preview' = { 56 | name: 'apimLoggerAccessPolicy' 57 | parent: eventHubForAPIM 58 | properties: { 59 | rights: [ 60 | 'Send' 61 | ] 62 | } 63 | } 64 | 65 | output eventHubForAPIMAuthorizationRuleName string = eventHubForAPIMAuthorizationRule.name 66 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/bicep/modules/keyvault.bicep: -------------------------------------------------------------------------------- 1 | @description('Name of the key vault to be created. Must be globally unique.') 2 | param keyVaultName string 3 | 4 | @description('Location for the resource.') 5 | param location string 6 | 7 | @description('Specifies all secrets wrapped in a secure object.') 8 | @secure() 9 | param secretsObject object 10 | 11 | // You have to have the tenantID of the Azure AD instance your Key Vault lives in for deployment. 12 | // The below var retrieves this information for us. 13 | var tenantId = subscription().tenantId 14 | 15 | resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { 16 | name: keyVaultName 17 | location: location 18 | properties: { 19 | tenantId: tenantId 20 | sku: { 21 | name: 'standard' 22 | family: 'A' 23 | } 24 | enableSoftDelete: true 25 | enableRbacAuthorization: true 26 | softDeleteRetentionInDays: 7 27 | enablePurgeProtection: true 28 | publicNetworkAccess: 'Enabled' 29 | } 30 | } 31 | 32 | resource secrets 'Microsoft.KeyVault/vaults/secrets@2021-04-01-preview' = [for secret in items(secretsObject): { 33 | name: secret.value.secretName 34 | parent: keyVault 35 | properties: { 36 | value: secret.value.secretValue 37 | } 38 | }] 39 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/bicep/modules/loganalytics.bicep: -------------------------------------------------------------------------------- 1 | @description('Name of the Log Analytics Workspace to be created.') 2 | param logAnalyticsWorkspaceName string 3 | 4 | @description('Location for the resource.') 5 | param location string 6 | 7 | resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { 8 | name: logAnalyticsWorkspaceName 9 | location: location 10 | properties: { 11 | sku: { 12 | name: 'pergb2018' 13 | } 14 | retentionInDays: 30 15 | features: { 16 | enableLogAccessUsingOnlyResourcePermissions: true 17 | } 18 | publicNetworkAccessForIngestion: 'Enabled' 19 | publicNetworkAccessForQuery: 'Enabled' 20 | } 21 | } 22 | 23 | // This outputs the Workspace ID of the Log Analytics Workspace we just created. This is different from the Resource ID. 24 | // This output is used in the creation of our Application Insights instance. 25 | 26 | output logAnalyticsWorkspaceId string = logAnalyticsWorkspace.id 27 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/bicep/modules/roleAssignment.bicep: -------------------------------------------------------------------------------- 1 | @description('principalId of the APIM Managed Identity. This is used to grant the APIM Managed Identity access to Key Vault.') 2 | param APIMPrincipalId string 3 | 4 | @description('Name of the Key Vault. This is used to grant the APIM Managed Identity access to Key Vault.') 5 | param keyVaultName string 6 | 7 | resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { 8 | name: keyVaultName 9 | } 10 | 11 | resource userRoleID 'Microsoft.Authorization/roleAssignments@2022-04-01' = { 12 | name: guid(APIMPrincipalId, '4633458b-17de-408a-b874-0445c86b69e6', keyVault.id) 13 | scope: keyVault 14 | properties: { 15 | roleDefinitionId: resourceId('Microsoft.Authorization/roleAssignments', '4633458b-17de-408a-b874-0445c86b69e6') 16 | principalId: APIMPrincipalId 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/bicep/modules/storageaccount.bicep: -------------------------------------------------------------------------------- 1 | @description('Name of the Storage Account to be created.') 2 | param storageAccountName string 3 | 4 | @description('Location for the resource.') 5 | param location string 6 | 7 | resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { 8 | name: storageAccountName 9 | location: location 10 | sku: { 11 | name: 'Standard_LRS' 12 | } 13 | kind: 'StorageV2' 14 | properties: { 15 | dnsEndpointType: 'Standard' 16 | defaultToOAuthAuthentication: false 17 | publicNetworkAccess: 'Enabled' 18 | allowCrossTenantReplication: false 19 | minimumTlsVersion: 'TLS1_2' 20 | allowBlobPublicAccess: false 21 | allowSharedKeyAccess: true 22 | supportsHttpsTrafficOnly: true 23 | accessTier: 'Hot' 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/bicep/modules/streamanalytics.bicep: -------------------------------------------------------------------------------- 1 | @description('Name of the Azure Stream Analytics to be created.') 2 | param streamAnalyticsName string 3 | 4 | @description('Location for the resource.') 5 | param location string 6 | 7 | resource streamAnalytics 'Microsoft.StreamAnalytics/streamingjobs@2021-10-01-preview' = { 8 | name: streamAnalyticsName 9 | location: location 10 | sku: { 11 | name: 'StandardV2' 12 | capacity: 3 13 | } 14 | identity: { 15 | type: 'SystemAssigned' 16 | } 17 | properties: { 18 | sku: { 19 | name: 'StandardV2' 20 | } 21 | eventsOutOfOrderPolicy: 'Adjust' 22 | outputErrorPolicy: 'Stop' 23 | eventsOutOfOrderMaxDelayInSeconds: 0 24 | eventsLateArrivalMaxDelayInSeconds: 5 25 | dataLocale: 'en-US' 26 | compatibilityLevel: '1.2' 27 | contentStoragePolicy: 'SystemAccount' 28 | jobType: 'Cloud' 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/endpoint_test.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "GPT-4-Turbo's joke: \n", 13 | "\n", 14 | "Why did the developer go broke after trying to monetize their API?\n", 15 | "\n", 16 | "Because they put all their resources into Azure API Management, but when they checked their balance, they realized it was a \"pay-as-you-go\" model, and all their calls were being redirected to the billing department!\n" 17 | ] 18 | } 19 | ], 20 | "source": [ 21 | "# This test is using v1.x of the Azure OpenAI Python SDK\n", 22 | "# https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/migration?tabs=python-new%2Cdalle-fix#chat-completions\n", 23 | "from openai import AzureOpenAI\n", 24 | "\n", 25 | "# Define the AzureOpenAI client object\n", 26 | "client = AzureOpenAI(\n", 27 | " azure_endpoint=\"https://{your_apim_resource_name}.azure-api.net/\",\n", 28 | " api_key=\"{your_apim_subscription_key}\",\n", 29 | " api_version=\"2023-12-01-preview\"\n", 30 | ")\n", 31 | "\n", 32 | "# Define the system role message to define our quick test assistant\n", 33 | "system_role_message = {\n", 34 | " \"role\": \"system\",\n", 35 | " \"content\": \"You are an AI assistant that helps people find information.\"\n", 36 | "}\n", 37 | "\n", 38 | "# Define the user role messages to pass to our quick assistant\n", 39 | "user_message_esg = {\n", 40 | " \"role\": \"user\",\n", 41 | " \"content\": \"Tell me a joke about Azure API Management.\"\n", 42 | "}\n", 43 | "\n", 44 | "# Prepare to laugh\n", 45 | "response_esg = client.chat.completions.create(\n", 46 | " model=\"gpt-4-turbo\",\n", 47 | " messages=[\n", 48 | " system_role_message,\n", 49 | " user_message_esg\n", 50 | " ],\n", 51 | " stream=False,\n", 52 | ")\n", 53 | "assistant_response_esg=response_esg.choices[0].message.content\n", 54 | "print(\"GPT-4-Turbo's joke: \" + \"\\n\" + \"\\n\" + assistant_response_esg)\n" 55 | ] 56 | } 57 | ], 58 | "metadata": { 59 | "kernelspec": { 60 | "display_name": "blackbauddemo", 61 | "language": "python", 62 | "name": "python3" 63 | }, 64 | "language_info": { 65 | "codemirror_mode": { 66 | "name": "ipython", 67 | "version": 3 68 | }, 69 | "file_extension": ".py", 70 | "mimetype": "text/x-python", 71 | "name": "python", 72 | "nbconvert_exporter": "python", 73 | "pygments_lexer": "ipython3", 74 | "version": "3.11.4" 75 | } 76 | }, 77 | "nbformat": 4, 78 | "nbformat_minor": 2 79 | } 80 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/endpoint_test.py: -------------------------------------------------------------------------------- 1 | # This test is using v1.x of the Azure OpenAI Python SDK 2 | # https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/migration?tabs=python-new%2Cdalle-fix#chat-completions 3 | from openai import AzureOpenAI 4 | 5 | # Define the AzureOpenAI client object 6 | client = AzureOpenAI( 7 | azure_endpoint="https://{your_apim_resource_name}.azure-api.net/", 8 | api_key="{your_apim_subscription_key}", 9 | api_version="2023-12-01-preview" 10 | ) 11 | 12 | # Define the system role message to define our quick test assistant 13 | system_role_message = { 14 | "role": "system", 15 | "content": "You are an AI assistant that helps people find information." 16 | } 17 | 18 | # Define the user role messages to pass to our quick assistant 19 | user_message_esg = { 20 | "role": "user", 21 | "content": "Tell me a joke about Azure API Management." 22 | } 23 | 24 | # Prepare to laugh 25 | response_esg = client.chat.completions.create( 26 | model="gpt-4-turbo", 27 | messages=[ 28 | system_role_message, 29 | user_message_esg 30 | ], 31 | stream=False, 32 | ) 33 | assistant_response_esg=response_esg.choices[0].message.content 34 | print("GPT-4-Turbo's joke: " + "\n" + "\n" + assistant_response_esg) -------------------------------------------------------------------------------- /foundation/apimv2-infra/terraform/modules/core/st/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_storage_account" "sa" { 2 | name = var.storage_account_name 3 | location = var.location 4 | resource_group_name = var.resource_group_name 5 | account_tier = "Standard" 6 | account_replication_type = "LRS" 7 | enable_https_traffic_only = true 8 | allow_nested_items_to_be_public = false 9 | min_tls_version = "TLS1_2" 10 | } 11 | 12 | # Create containers 13 | resource "azurerm_storage_container" "container" { 14 | count = length(var.container_names) 15 | name = var.container_names[count.index] 16 | container_access_type = "private" 17 | storage_account_name = azurerm_storage_account.sa.name 18 | } 19 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/terraform/modules/core/st/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" {} 2 | variable "location" {} 3 | variable "storage_account_name" {} 4 | variable "container_names" { 5 | default = [] 6 | } -------------------------------------------------------------------------------- /foundation/apimv2-infra/terraform/modules/integration/apim/main.tf: -------------------------------------------------------------------------------- 1 | data "azurerm_resource_group" "rg" { 2 | name = var.resource_group_name 3 | } 4 | 5 | resource "azapi_resource" "apim" { 6 | type = "Microsoft.ApiManagement/service@2023-05-01-preview" 7 | name = var.apim_name 8 | parent_id = data.azurerm_resource_group.rg.id 9 | location = var.location 10 | identity { 11 | type = "SystemAssigned" 12 | } 13 | schema_validation_enabled = false # requiered for now 14 | body = jsonencode({ 15 | sku = { 16 | name = "StandardV2" 17 | capacity = 1 18 | } 19 | properties = { 20 | publisherEmail = var.publisher_email 21 | publisherName = var.publisher_name 22 | apiVersionConstraint = {} 23 | developerPortalStatus = "Disabled" 24 | } 25 | }) 26 | response_export_values = [ 27 | "identity.principalId", 28 | "properties.gatewayUrl" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/terraform/modules/integration/apim/outputs.tf: -------------------------------------------------------------------------------- 1 | output "apim_name" { 2 | value = azapi_resource.apim.name 3 | } 4 | 5 | output "apim_id" { 6 | value = azapi_resource.apim.id 7 | } 8 | 9 | output "gateway_url" { 10 | value = jsondecode(azapi_resource.apim.output).properties.gatewayUrl 11 | } 12 | 13 | output "principal_id" { 14 | value = jsondecode(azapi_resource.apim.output).identity.principalId 15 | } 16 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/terraform/modules/integration/apim/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | source = "hashicorp/azurerm" 5 | version = "3.87.0" 6 | } 7 | azapi = { 8 | source = "azure/azapi" 9 | version = "= 1.0.0" 10 | } 11 | random = { 12 | source = "hashicorp/random" 13 | version = "3.6.0" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/terraform/modules/integration/apim/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" {} 2 | variable "location" {} 3 | variable "apim_name" {} 4 | variable "publisher_name" {} 5 | variable "publisher_email" {} 6 | 7 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/terraform/modules/integration/apim_configuration/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | azurerm = { 4 | source = "hashicorp/azurerm" 5 | version = "3.87.0" 6 | } 7 | azapi = { 8 | source = "azure/azapi" 9 | version = "= 1.0.0" 10 | } 11 | random = { 12 | source = "hashicorp/random" 13 | version = "3.6.0" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/terraform/modules/integration/apim_configuration/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" {} 2 | variable "location" {} 3 | variable "apim_name" {} 4 | variable "apim_id" {} 5 | variable "gatewayUrl" {} 6 | variable "appi_resource_id" {} 7 | variable "appi_instrumentation_key" {} 8 | variable "keyvault_name" {} 9 | variable "eventhub_connection_string" {} 10 | variable "eventhub_name" {} 11 | variable "secrets" { 12 | default = {} 13 | } 14 | -------------------------------------------------------------------------------- /foundation/apimv2-infra/terraform/modules/integration/asa/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_stream_analytics_job" "this" { 2 | name = var.stream_analytics_name 3 | resource_group_name = var.resource_group_name 4 | location = var.location 5 | streaming_units = 3 6 | identity { 7 | type = "SystemAssigned" 8 | } 9 | events_out_of_order_max_delay_in_seconds = 0 10 | events_late_arrival_max_delay_in_seconds = 5 11 | data_locale = "en-US" 12 | events_out_of_order_policy = "Adjust" 13 | output_error_policy = "Stop" 14 | compatibility_level = "1.2" 15 | content_storage_policy = "SystemAccount" 16 | type = "Cloud" 17 | 18 | transformation_query = < any: 3 | raise NotImplementedError 4 | -------------------------------------------------------------------------------- /foundation/standalone/app/backend/FlaskApp/data/employeeinfo.csv: -------------------------------------------------------------------------------- 1 | name,title,insurance,insurancegroup 2 | Employee1,Program Manager,Northwind Health Plus,Family 3 | Employee2,Software Engineer,Northwind Health Plus,Single 4 | Employee3,Software Engineer,Northwind Health Standard,Family 5 | -------------------------------------------------------------------------------- /foundation/standalone/app/backend/FlaskApp/lookuptool.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | import csv 3 | from langchain.agents import Tool, tool 4 | from typing import Optional 5 | import pandas as pd 6 | import logging 7 | from langchain.utilities import BingSearchAPIWrapper 8 | from .clients import BING_SUBSCRIPTION_KEY, BING_SEARCH_URL 9 | 10 | class CsvLookupTool(Tool): 11 | def __init__(self, filename: path, key_field: str, name: str = "lookup", description: str = "useful to look up details given an input key as opposite to searching data with an unstructured question"): 12 | super().__init__(name, self.lookup, description) # type: ignore 13 | self.data = {} 14 | with open(filename, newline='') as csvfile: # type: ignore 15 | reader = csv.DictReader(csvfile) 16 | for row in reader: 17 | self.data[row[key_field]] = "\n".join([f"{i}:{row[i]}" for i in row]) 18 | 19 | def lookup(self, key: str) -> Optional[str]: 20 | return self.data.get(key, "") 21 | 22 | @tool 23 | def pandas_lookup(query: str,filename:str = 'FlaskApp/data/employeeinfo.csv') -> Optional[str]: 24 | """ 25 | Looks up details about employees and their info in a pandas dataframe. 26 | 27 | Args: 28 | filename (str): The name of the CSV file containing the data. 29 | query (str): The name of the employee to look up. 30 | 31 | Returns: 32 | str: A string representation of the rows in the dataframe that match the query. 33 | Returns "No results found." if no matches are found. 34 | """ 35 | df = pd.read_csv(filename) 36 | try: 37 | result = df.loc[df['name'].str.lower() == query.lower()] 38 | response = result.to_string(index=False) 39 | except IndexError: 40 | response= f"{query} is not in pandas dataframe" 41 | # logging.info("-----------------------") 42 | # logging.info("response from pandas_lookup: " + response) 43 | return response 44 | 45 | @tool 46 | def web_search(query: str) -> Optional[str]: 47 | """ 48 | Looks up bing search for latest and relevant news. 49 | 50 | Args: 51 | query (str): search keyword. 52 | 53 | Returns: 54 | str: A string representation of search results from bing. 55 | """ 56 | search = BingSearchAPIWrapper(bing_subscription_key=BING_SUBSCRIPTION_KEY, bing_search_url=BING_SEARCH_URL) 57 | return search.run(query) 58 | -------------------------------------------------------------------------------- /foundation/standalone/app/backend/FlaskApp/requirements.txt: -------------------------------------------------------------------------------- 1 | # Do not include azure-functions-worker in this file 2 | # The Python Worker is managed by the Azure Functions platform 3 | # Manually managing azure-functions-worker may cause unexpected issues 4 | 5 | azure-functions 6 | Flask==3.0.1 7 | langchain==0.0.78 8 | azure-search-documents==11.4.0b3 9 | azure-storage-blob==12.14.1 10 | azure-ai-formrecognizer==3.2.1 11 | pypdf==3.5.0 12 | filetype==1.2.0 13 | azure-identity>=1.15.0 14 | azure-search-documents>=11.0.0 15 | azure-storage-blob>=12.0.0 16 | httpx==0.23.0 17 | httpcore==0.15.0 -------------------------------------------------------------------------------- /foundation/standalone/app/backend/FlaskApp/text.py: -------------------------------------------------------------------------------- 1 | def nonewlines(s: str) -> str: 2 | return s.replace('\n', ' ').replace('\r', ' ') 3 | -------------------------------------------------------------------------------- /foundation/standalone/app/backend/HandleApproach/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import azure.functions as func 3 | from ..FlaskApp import app 4 | 5 | logging.info("Python HTTP HandleApproach trigger - Entry point initialized.") 6 | 7 | def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse: 8 | """Each request is redirected to the WSGI handler. 9 | """ 10 | logging.info(f"Python HTTP trigger function processed a request. RequestUri={req.url}") 11 | # logging.info(f"url map: {app.url_map}") 12 | return func.WsgiMiddleware(app.wsgi_app).handle(req, context) 13 | -------------------------------------------------------------------------------- /foundation/standalone/app/backend/HandleApproach/function.json: -------------------------------------------------------------------------------- 1 | { 2 | "scriptFile": "__init__.py", 3 | "bindings": [ 4 | { 5 | "authLevel": "anonymous", 6 | "type": "httpTrigger", 7 | "direction": "in", 8 | "name": "req", 9 | "methods": [ 10 | "get", 11 | "post" 12 | ], 13 | "route": "{*route}" 14 | }, 15 | { 16 | "type": "http", 17 | "direction": "out", 18 | "name": "$return" 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /foundation/standalone/app/backend/getting_started.md: -------------------------------------------------------------------------------- 1 | ## Getting Started with Azure Function 2 | ### Last updated: March 8th 2021 3 | 4 | #### Project Structure 5 | The main project folder () can contain the following files: 6 | 7 | * **local.settings.json** - Used to store app settings and connection strings when running locally. This file doesn't get published to Azure. To learn more, see [local.settings.file](https://aka.ms/azure-functions/python/local-settings). 8 | * **requirements.txt** - Contains the list of Python packages the system installs when publishing to Azure. 9 | * **host.json** - Contains global configuration options that affect all functions in a function app. This file does get published to Azure. Not all options are supported when running locally. To learn more, see [host.json](https://aka.ms/azure-functions/python/host.json). 10 | * **.vscode/** - (Optional) Contains store VSCode configuration. To learn more, see [VSCode setting](https://aka.ms/azure-functions/python/vscode-getting-started). 11 | * **.venv/** - (Optional) Contains a Python virtual environment used by local development. 12 | * **Dockerfile** - (Optional) Used when publishing your project in a [custom container](https://aka.ms/azure-functions/python/custom-container). 13 | * **tests/** - (Optional) Contains the test cases of your function app. For more information, see [Unit Testing](https://aka.ms/azure-functions/python/unit-testing). 14 | * **.funcignore** - (Optional) Declares files that shouldn't get published to Azure. Usually, this file contains .vscode/ to ignore your editor setting, .venv/ to ignore local Python virtual environment, tests/ to ignore test cases, and local.settings.json to prevent local app settings being published. 15 | 16 | Each function has its own code file and binding configuration file ([**function.json**](https://aka.ms/azure-functions/python/function.json)). 17 | 18 | #### Developing your first Python function using VS Code 19 | 20 | If you have not already, please checkout our [quickstart](https://aka.ms/azure-functions/python/quickstart) to get you started with Azure Functions developments in Python. 21 | 22 | #### Publishing your function app to Azure 23 | 24 | For more information on deployment options for Azure Functions, please visit this [guide](https://docs.microsoft.com/en-us/azure/azure-functions/create-first-function-vs-code-python#publish-the-project-to-azure). 25 | 26 | #### Next Steps 27 | 28 | * To learn more about developing Azure Functions, please visit [Azure Functions Developer Guide](https://aka.ms/azure-functions/python/developer-guide). 29 | 30 | * To learn more specific guidance on developing Azure Functions with Python, please visit [Azure Functions Developer Python Guide](https://aka.ms/azure-functions/python/python-developer-guide). -------------------------------------------------------------------------------- /foundation/standalone/app/backend/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingSettings": { 6 | "isEnabled": true, 7 | "excludedTypes": "Request" 8 | } 9 | } 10 | }, 11 | "extensionBundle": { 12 | "id": "Microsoft.Azure.Functions.ExtensionBundle", 13 | "version": "[3.*, 4.0.0)" 14 | }, 15 | "extensions": { 16 | "http": { 17 | "routePrefix": "api" 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /foundation/standalone/app/backend/requirements.txt: -------------------------------------------------------------------------------- 1 | # Do not include azure-functions-worker in this file 2 | # The Python Worker is managed by the Azure Functions platform 3 | # Manually managing azure-functions-worker may cause unexpected issues 4 | 5 | azure-functions 6 | Flask==3.0.1 7 | langchain==0.0.141 8 | azure-search-documents==11.4.0b3 9 | azure-storage-blob==12.14.1 10 | azure-ai-formrecognizer==3.2.1 11 | pypdf==3.5.0 12 | pandas==2.0.0 13 | filetype==1.2.0 14 | openai>=1.8.0 15 | azure-identity>=1.15.0 16 | azure-search-documents>=11.0.0 17 | azure-storage-blob>=12.0.0 18 | httpx==0.23.0 19 | httpcore==0.15.0 -------------------------------------------------------------------------------- /foundation/standalone/app/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Azure az webapp deployment details 2 | .azure 3 | *_env 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | cover/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | .pybuilder/ 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | # For a library or package, you might want to ignore these files since the code is 91 | # intended to run in multiple environments; otherwise, check them in: 92 | # .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 102 | __pypackages__/ 103 | 104 | # Celery stuff 105 | celerybeat-schedule 106 | celerybeat.pid 107 | 108 | # SageMath parsed files 109 | *.sage.py 110 | 111 | # Environments 112 | .env 113 | .venv 114 | env/ 115 | venv/ 116 | ENV/ 117 | env.bak/ 118 | venv.bak/ 119 | 120 | # Spyder project settings 121 | .spyderproject 122 | .spyproject 123 | 124 | # Rope project settings 125 | .ropeproject 126 | 127 | # mkdocs documentation 128 | /site 129 | 130 | # mypy 131 | .mypy_cache/ 132 | .dmypy.json 133 | dmypy.json 134 | 135 | # Pyre type checker 136 | .pyre/ 137 | 138 | # pytype static type analyzer 139 | .pytype/ 140 | 141 | # Cython debug symbols 142 | cython_debug/ 143 | 144 | # NPM 145 | npm-debug.log* 146 | node_modules 147 | static/ -------------------------------------------------------------------------------- /foundation/standalone/app/frontend/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "printWidth": 160, 4 | "arrowParens": "avoid", 5 | "trailingComma": "none" 6 | } 7 | -------------------------------------------------------------------------------- /foundation/standalone/app/frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ChatGPT + Enterprise data 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /foundation/standalone/app/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview", 10 | "watch": "tsc && vite build --watch" 11 | }, 12 | "dependencies": { 13 | "@fluentui/react": "^8.105.3", 14 | "@fluentui/react-icons": "^2.0.195", 15 | "@react-spring/web": "^9.7.1", 16 | "dompurify": "^3.0.1", 17 | "react": "^18.2.0", 18 | "react-dom": "^18.2.0", 19 | "react-dropzone": "^14.2.3", 20 | "react-router-dom": "^6.8.1" 21 | }, 22 | "devDependencies": { 23 | "@azure/static-web-apps-cli": "^2.0.1", 24 | "@types/dompurify": "^2.4.0", 25 | "@types/node": "^18.15.11", 26 | "@types/react": "^18.0.28", 27 | "@types/react-dom": "^18.0.11", 28 | "@vitejs/plugin-react": "^3.1.0", 29 | "prettier": "^2.8.3", 30 | "typescript": "^4.9.3", 31 | "vite": "^4.2.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /foundation/standalone/app/frontend/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /foundation/standalone/app/frontend/src/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./api"; 2 | export * from "./models"; -------------------------------------------------------------------------------- /foundation/standalone/app/frontend/src/api/models.ts: -------------------------------------------------------------------------------- 1 | export const enum Approaches { 2 | RetrieveThenRead = "rtr", 3 | ReadRetrieveRead = "rrr", 4 | ReadDecomposeAsk = "rda" 5 | } 6 | 7 | export type AskRequestOverrides = { 8 | semanticRanker?: boolean; 9 | semanticCaptions?: boolean; 10 | excludeCategory?: string; 11 | top?: number; 12 | temperature?: number; 13 | promptTemplate?: string; 14 | promptTemplatePrefix?: string; 15 | promptTemplateSuffix?: string; 16 | suggestFollowupQuestions?: boolean; 17 | }; 18 | 19 | export type AskRequest = { 20 | question: string; 21 | approach: Approaches; 22 | overrides?: AskRequestOverrides; 23 | }; 24 | 25 | export type AskResponse = { 26 | answer: string; 27 | thoughts: string | null; 28 | data_points: string[]; 29 | error?: string; 30 | }; 31 | 32 | export type ChatTurn = { 33 | user: string; 34 | bot?: string; 35 | }; 36 | 37 | export type ChatRequest = { 38 | history: ChatTurn[]; 39 | approach: Approaches; 40 | overrides?: AskRequestOverrides; 41 | }; 42 | 43 | export type UploadFileRequest = { 44 | formData: FormData; 45 | }; 46 | 47 | export type UploadFileResponse = { 48 | success: boolean; 49 | message?: string; 50 | error?: string; 51 | }; -------------------------------------------------------------------------------- /foundation/standalone/app/frontend/src/assets/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /foundation/standalone/app/frontend/src/assets/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /foundation/standalone/app/frontend/src/components/AnalysisPanel/AnalysisPanel.module.css: -------------------------------------------------------------------------------- 1 | .thoughtProcess { 2 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; 3 | word-wrap: break-word; 4 | padding-top: 12px; 5 | padding-bottom: 12px; 6 | } 7 | -------------------------------------------------------------------------------- /foundation/standalone/app/frontend/src/components/AnalysisPanel/AnalysisPanel.tsx: -------------------------------------------------------------------------------- 1 | import { Pivot, PivotItem } from "@fluentui/react"; 2 | import DOMPurify from "dompurify"; 3 | 4 | import styles from "./AnalysisPanel.module.css"; 5 | 6 | import { SupportingContent } from "../SupportingContent"; 7 | import { AskResponse } from "../../api"; 8 | import { AnalysisPanelTabs } from "./AnalysisPanelTabs"; 9 | 10 | interface Props { 11 | className: string; 12 | activeTab: AnalysisPanelTabs; 13 | onActiveTabChanged: (tab: AnalysisPanelTabs) => void; 14 | activeCitation: string | undefined; 15 | citationHeight: string; 16 | answer: AskResponse; 17 | } 18 | 19 | const pivotItemDisabledStyle = { disabled: true, style: { color: "grey" } }; 20 | 21 | export const AnalysisPanel = ({ answer, activeTab, activeCitation, citationHeight, className, onActiveTabChanged }: Props) => { 22 | const isDisabledThoughtProcessTab: boolean = !answer.thoughts; 23 | const isDisabledSupportingContentTab: boolean = !answer.data_points.length; 24 | const isDisabledCitationTab: boolean = !activeCitation; 25 | 26 | const sanitizedThoughts = DOMPurify.sanitize(answer.thoughts!); 27 | 28 | return ( 29 | pivotItem && onActiveTabChanged(pivotItem.props.itemKey! as AnalysisPanelTabs)} 33 | > 34 | 39 |
40 |
41 | 46 | 47 | 48 | 53 |