diff --git a/.gitignore b/.gitignore index 3c3f4e6..78a9f71 100644 --- a/.gitignore +++ b/.gitignore @@ -261,4 +261,9 @@ paket-files/ # Python Tools for Visual Studio (PTVS) __pycache__/ -*.pyc \ No newline at end of file +*.pyc + +# Environment variables +.env + +.azure \ No newline at end of file diff --git a/README.md b/README.md index d8246d3..c693747 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # aks-docs-gpt-chat A GPT Chat application for Azure Kubernetes Service Documentation +## Deploy + +- Using the Azure Developer CLI ## Application Diagram diff --git a/aks-docs-gpt-chat.sln b/aks-docs-gpt-chat.sln new file mode 100644 index 0000000..60a4bdf --- /dev/null +++ b/aks-docs-gpt-chat.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AksChatBot", "src\AksChatBot.csproj", "{054E6CFD-3E6D-49F2-8E52-43E302056DC1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {054E6CFD-3E6D-49F2-8E52-43E302056DC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {054E6CFD-3E6D-49F2-8E52-43E302056DC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {054E6CFD-3E6D-49F2-8E52-43E302056DC1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {054E6CFD-3E6D-49F2-8E52-43E302056DC1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {03C59C30-345F-4D4B-923D-CC3E563B6A00} + EndGlobalSection +EndGlobal diff --git a/azure.yaml b/azure.yaml new file mode 100644 index 0000000..a3c8760 --- /dev/null +++ b/azure.yaml @@ -0,0 +1,8 @@ +name: gbb-aks-llm-chat-bot +metadata: + template: gbb-aks-llm-chat-bot@0.0.1-beta +services: + api: + project: ./src + language: dotnet + host: appservice \ No newline at end of file diff --git a/infra/ai/cognitive-services.bicep b/infra/ai/cognitive-services.bicep new file mode 100644 index 0000000..e69de29 diff --git a/infra/app/app-service-app-settings.bicep b/infra/app/app-service-app-settings.bicep new file mode 100644 index 0000000..f4b22f8 --- /dev/null +++ b/infra/app/app-service-app-settings.bicep @@ -0,0 +1,17 @@ +metadata description = 'Updates app settings for an Azure App Service.' +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +@secure() +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/infra/app/app-service-plan.bicep b/infra/app/app-service-plan.bicep new file mode 100644 index 0000000..2e37e04 --- /dev/null +++ b/infra/app/app-service-plan.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/infra/app/app-service.bicep b/infra/app/app-service.bicep new file mode 100644 index 0000000..957a431 --- /dev/null +++ b/infra/app/app-service.bicep @@ -0,0 +1,118 @@ +metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource configLogs 'config' = { + name: 'logs' + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + } + + resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { + name: 'ftp' + properties: { + allow: false + } + } + + resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { + name: 'scm' + properties: { + allow: false + } + } +} + +module config './app-service-app-settings.bicep' = if (!empty(appSettings)) { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {}, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/infra/app/container-registry.bicep b/infra/app/container-registry.bicep new file mode 100644 index 0000000..e69de29 diff --git a/infra/data/cognitive-search.bicep b/infra/data/cognitive-search.bicep new file mode 100644 index 0000000..e69de29 diff --git a/infra/main.bicep b/infra/main.bicep new file mode 100644 index 0000000..56b71d3 --- /dev/null +++ b/infra/main.bicep @@ -0,0 +1,65 @@ +targetScope = 'subscription' + +@minLength(3) +@maxLength(3) +@description('A prefix string to generate a short unique hash used in all resources.') +param prefix string = 'gbb' + +@minLength(4) +@description('Primary location for all resources') +param location string = 'eastus2' + +@description('Resource Group to deploy all resources to') +param resourceGroupName string = '${prefix}-rg' + +@description('Name of the App Service Plan to create') +param appServicePlanName string = '' + +@description('Name of the API Service to create') +param apiServiceName string = '' + +// @description('Name of the front end UI app to create') +// param frontEndName string = 'frontend' + +var resourceToken = toLower(uniqueString(subscription().id, prefix, location)) + +// Organize resources in a resource group +resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// Create an App Service Plan to group applications under the same payment plan and SKU +module appServicePlan './app/app-service-plan.bicep' = { + name: 'appserviceplan' + scope: rg + params: { + name: !empty(appServicePlanName) ? appServicePlanName : resourceToken + location: location + sku: { + name: 'B1' + } + } +} + +module appService './app/app-service.bicep' = { + name: 'appservice' + scope: rg + dependsOn: [ + appServicePlan + ] + params: { + name: !empty(apiServiceName) ? apiServiceName : resourceToken + location: location + appServicePlanId: appServicePlan.outputs.id + runtimeName: 'dotnet-isolated' + runtimeVersion: '7.0' + tags: { + 'azd-service-name': apiServiceName + } + } +} + +// Data outputs +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId diff --git a/infra/main.parameters.json b/infra/main.parameters.json new file mode 100644 index 0000000..dee1bec --- /dev/null +++ b/infra/main.parameters.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + } + } + } \ No newline at end of file diff --git a/rest-samples/query-aks.http b/rest-samples/query-aks.http index 925d1e2..7d29ca9 100644 --- a/rest-samples/query-aks.http +++ b/rest-samples/query-aks.http @@ -1,4 +1,4 @@ -POST http://localhost:7071/api/Chat +POST http://localhost:8080/api/Chat Content-Type: application/json { diff --git a/src/.dockerignore b/src/.dockerignore index 1927772..976dca8 100644 --- a/src/.dockerignore +++ b/src/.dockerignore @@ -1 +1,2 @@ -local.settings.json \ No newline at end of file +local.settings.json +.env \ No newline at end of file diff --git a/src/AksChatBot.csproj b/src/AksChatBot.csproj index 3a07da8..b4e16cd 100644 --- a/src/AksChatBot.csproj +++ b/src/AksChatBot.csproj @@ -5,6 +5,7 @@ Exe enable enable + 837887e9-bab1-40c9-8261-3f8ca0d1c5a0