From f9cc3602e4668dbae8333a21cef53e5b2461dc45 Mon Sep 17 00:00:00 2001 From: Jordan Matthiesen <1333029+jmatthiesen@users.noreply.github.com> Date: Sun, 13 Oct 2024 16:21:51 -0700 Subject: [PATCH 1/7] Adding initial version of sample. --- .gitattributes | 3 + .github/workflows/azure-dev.yml | 63 + .gitignore | 10 + .vscode/launch.json | 15 + CODE_OF_CONDUCT.md | 9 + LICENSE | 21 + SECURITY.md | 41 + .../AIChatApp.AppHost.csproj | 23 + src/AIChatApp.AppHost/Program.cs | 21 + .../Properties/launchSettings.json | 29 + .../appsettings.Development.json | 8 + src/AIChatApp.AppHost/appsettings.json | 9 + .../AIChatApp.Backend.csproj | 25 + src/AIChatApp.Backend/AIChatApp.Backend.http | 6 + src/AIChatApp.Backend/Program.cs | 46 + .../Properties/launchSettings.json | 41 + src/AIChatApp.Backend/Services/ChatService.cs | 59 + .../appsettings.Development.json | 8 + src/AIChatApp.Backend/appsettings.json | 9 + .../AIChatApp.Shared.csproj | 9 + src/AIChatApp.DataEntities/BackendClient.cs | 28 + src/AIChatApp.DataEntities/ChatModel.cs | 19 + src/AIChatApp.Web/AIChatApp.Web.csproj | 15 + src/AIChatApp.Web/Components/App.razor | 24 + src/AIChatApp.Web/Components/Chat/Chat.razor | 27 + .../Components/Chat/Chat.razor.cs | 67 + .../Components/Chat/Chat.razor.css | 38 + .../Components/Chat/Chat.razor.js | 14 + .../Components/Chat/ChatMessage.razor | 25 + .../Components/Chat/ChatMessage.razor.css | 101 + .../Components/Layout/MainLayout.razor | 23 + .../Components/Layout/MainLayout.razor.css | 96 + .../Components/Layout/NavMenu.razor | 18 + .../Components/Layout/NavMenu.razor.css | 105 + .../Components/Pages/Error.razor | 36 + src/AIChatApp.Web/Components/Pages/Home.razor | 8 + src/AIChatApp.Web/Components/Routes.razor | 6 + src/AIChatApp.Web/Components/_Imports.razor | 10 + src/AIChatApp.Web/Program.cs | 29 + .../Properties/launchSettings.json | 38 + .../appsettings.Development.json | 9 + src/AIChatApp.Web/appsettings.json | 9 + src/AIChatApp.Web/wwwroot/app.css | 52 + .../wwwroot/bootstrap/bootstrap.min.css | 7 + .../wwwroot/bootstrap/bootstrap.min.css.map | 1 + src/AIChatApp.Web/wwwroot/css/site.css | 42 + src/AIChatApp.Web/wwwroot/favicon.ico | Bin 0 -> 5430 bytes src/AIChatApp.Web/wwwroot/js/site.js | 4 + .../wwwroot/lib/bootstrap/LICENSE | 22 + .../lib/bootstrap/dist/css/bootstrap-grid.css | 4997 +++++++ .../bootstrap/dist/css/bootstrap-grid.css.map | 1 + .../bootstrap/dist/css/bootstrap-grid.min.css | 7 + .../dist/css/bootstrap-grid.min.css.map | 1 + .../bootstrap/dist/css/bootstrap-grid.rtl.css | 4996 +++++++ .../dist/css/bootstrap-grid.rtl.css.map | 1 + .../dist/css/bootstrap-grid.rtl.min.css | 7 + .../dist/css/bootstrap-grid.rtl.min.css.map | 1 + .../bootstrap/dist/css/bootstrap-reboot.css | 427 + .../dist/css/bootstrap-reboot.css.map | 1 + .../dist/css/bootstrap-reboot.min.css | 8 + .../dist/css/bootstrap-reboot.min.css.map | 1 + .../dist/css/bootstrap-reboot.rtl.css | 424 + .../dist/css/bootstrap-reboot.rtl.css.map | 1 + .../dist/css/bootstrap-reboot.rtl.min.css | 8 + .../dist/css/bootstrap-reboot.rtl.min.css.map | 1 + .../dist/css/bootstrap-utilities.css | 4866 +++++++ .../dist/css/bootstrap-utilities.css.map | 1 + .../dist/css/bootstrap-utilities.min.css | 7 + .../dist/css/bootstrap-utilities.min.css.map | 1 + .../dist/css/bootstrap-utilities.rtl.css | 4857 +++++++ .../dist/css/bootstrap-utilities.rtl.css.map | 1 + .../dist/css/bootstrap-utilities.rtl.min.css | 7 + .../css/bootstrap-utilities.rtl.min.css.map | 1 + .../lib/bootstrap/dist/css/bootstrap.css | 11221 ++++++++++++++++ .../lib/bootstrap/dist/css/bootstrap.css.map | 1 + .../lib/bootstrap/dist/css/bootstrap.min.css | 7 + .../bootstrap/dist/css/bootstrap.min.css.map | 1 + .../lib/bootstrap/dist/css/bootstrap.rtl.css | 11197 +++++++++++++++ .../bootstrap/dist/css/bootstrap.rtl.css.map | 1 + .../bootstrap/dist/css/bootstrap.rtl.min.css | 7 + .../dist/css/bootstrap.rtl.min.css.map | 1 + .../lib/bootstrap/dist/js/bootstrap.bundle.js | 6780 ++++++++++ .../bootstrap/dist/js/bootstrap.bundle.js.map | 1 + .../bootstrap/dist/js/bootstrap.bundle.min.js | 7 + .../dist/js/bootstrap.bundle.min.js.map | 1 + .../lib/bootstrap/dist/js/bootstrap.esm.js | 4977 +++++++ .../bootstrap/dist/js/bootstrap.esm.js.map | 1 + .../bootstrap/dist/js/bootstrap.esm.min.js | 7 + .../dist/js/bootstrap.esm.min.js.map | 1 + .../lib/bootstrap/dist/js/bootstrap.js | 5026 +++++++ .../lib/bootstrap/dist/js/bootstrap.js.map | 1 + .../lib/bootstrap/dist/js/bootstrap.min.js | 7 + .../bootstrap/dist/js/bootstrap.min.js.map | 1 + src/ai-chat-aspire-sk.sln | 43 + 94 files changed, 61240 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/workflows/azure-dev.yml create mode 100644 .vscode/launch.json create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE create mode 100644 SECURITY.md create mode 100644 src/AIChatApp.AppHost/AIChatApp.AppHost.csproj create mode 100644 src/AIChatApp.AppHost/Program.cs create mode 100644 src/AIChatApp.AppHost/Properties/launchSettings.json create mode 100644 src/AIChatApp.AppHost/appsettings.Development.json create mode 100644 src/AIChatApp.AppHost/appsettings.json create mode 100644 src/AIChatApp.Backend/AIChatApp.Backend.csproj create mode 100644 src/AIChatApp.Backend/AIChatApp.Backend.http create mode 100644 src/AIChatApp.Backend/Program.cs create mode 100644 src/AIChatApp.Backend/Properties/launchSettings.json create mode 100644 src/AIChatApp.Backend/Services/ChatService.cs create mode 100644 src/AIChatApp.Backend/appsettings.Development.json create mode 100644 src/AIChatApp.Backend/appsettings.json create mode 100644 src/AIChatApp.DataEntities/AIChatApp.Shared.csproj create mode 100644 src/AIChatApp.DataEntities/BackendClient.cs create mode 100644 src/AIChatApp.DataEntities/ChatModel.cs create mode 100644 src/AIChatApp.Web/AIChatApp.Web.csproj create mode 100644 src/AIChatApp.Web/Components/App.razor create mode 100644 src/AIChatApp.Web/Components/Chat/Chat.razor create mode 100644 src/AIChatApp.Web/Components/Chat/Chat.razor.cs create mode 100644 src/AIChatApp.Web/Components/Chat/Chat.razor.css create mode 100644 src/AIChatApp.Web/Components/Chat/Chat.razor.js create mode 100644 src/AIChatApp.Web/Components/Chat/ChatMessage.razor create mode 100644 src/AIChatApp.Web/Components/Chat/ChatMessage.razor.css create mode 100644 src/AIChatApp.Web/Components/Layout/MainLayout.razor create mode 100644 src/AIChatApp.Web/Components/Layout/MainLayout.razor.css create mode 100644 src/AIChatApp.Web/Components/Layout/NavMenu.razor create mode 100644 src/AIChatApp.Web/Components/Layout/NavMenu.razor.css create mode 100644 src/AIChatApp.Web/Components/Pages/Error.razor create mode 100644 src/AIChatApp.Web/Components/Pages/Home.razor create mode 100644 src/AIChatApp.Web/Components/Routes.razor create mode 100644 src/AIChatApp.Web/Components/_Imports.razor create mode 100644 src/AIChatApp.Web/Program.cs create mode 100644 src/AIChatApp.Web/Properties/launchSettings.json create mode 100644 src/AIChatApp.Web/appsettings.Development.json create mode 100644 src/AIChatApp.Web/appsettings.json create mode 100644 src/AIChatApp.Web/wwwroot/app.css create mode 100644 src/AIChatApp.Web/wwwroot/bootstrap/bootstrap.min.css create mode 100644 src/AIChatApp.Web/wwwroot/bootstrap/bootstrap.min.css.map create mode 100644 src/AIChatApp.Web/wwwroot/css/site.css create mode 100644 src/AIChatApp.Web/wwwroot/favicon.ico create mode 100644 src/AIChatApp.Web/wwwroot/js/site.js create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/LICENSE create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js create mode 100644 src/AIChatApp.Web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map create mode 100644 src/ai-chat-aspire-sk.sln diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5dc46e6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +* text=auto eol=lf +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf \ No newline at end of file diff --git a/.github/workflows/azure-dev.yml b/.github/workflows/azure-dev.yml new file mode 100644 index 0000000..0c5baf5 --- /dev/null +++ b/.github/workflows/azure-dev.yml @@ -0,0 +1,63 @@ +on: + workflow_dispatch: + push: + branches: + - main + +permissions: + id-token: write + contents: read + actions: read + security-events: write + +jobs: + deploy: + runs-on: ubuntu-latest + env: + AZURE_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }} + AZURE_TENANT_ID: ${{ vars.AZURE_TENANT_ID }} + AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }} + AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }} + AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install azd + uses: Azure/setup-azd@v1.0.0 + + #- name: Run Microsoft Security DevOps Analysis + # uses: microsoft/security-devops-action@v1 + # id: msdo + # with: + # tools: templateanalyzer + + #- name: Upload results to Security tab + # uses: github/codeql-action/upload-sarif@v2 + # with: + # sarif_file: ${{ steps.msdo.outputs.sarifFile }} + + - name: Log in with Azure (Federated Credentials) + if: ${{ env.AZURE_CLIENT_ID != '' }} + run: | + azd auth login ` + --client-id "$Env:AZURE_CLIENT_ID" ` + --federated-credential-provider "github" ` + --tenant-id "$Env:AZURE_TENANT_ID" + shell: pwsh + + - name: Provision Infrastructure + run: azd provision --no-prompt + env: + AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }} + AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} + AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }} + CREATE_ROLE_FOR_USER: false + + - name: Deploy Application + run: azd deploy --no-prompt + env: + AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }} + AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} + AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }} diff --git a/.gitignore b/.gitignore index 8a30d25..1b1ef42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,10 @@ +# Azure Developer CLI generated folder +# including environment variables +.azure + +# User-specific configuration +appSettings.local.json + ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## @@ -396,3 +403,6 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml + +# AZD +.[aA]zure/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..100b77b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "C#: AppHost Debug", + "type": "dotnet", + "request": "launch", + "projectPath": "${workspaceFolder}/src/AIChatApp.AppHost/AIChatApp.AppHost.csproj" + } + + ] +} \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..6257f2e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,9 @@ +# Microsoft Open Source Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b12fb48 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright 2022 (c) Microsoft Corporation. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..b3c89ef --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +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). + +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. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +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). + +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). + +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). + +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: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +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. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). + + diff --git a/src/AIChatApp.AppHost/AIChatApp.AppHost.csproj b/src/AIChatApp.AppHost/AIChatApp.AppHost.csproj new file mode 100644 index 0000000..1628d2f --- /dev/null +++ b/src/AIChatApp.AppHost/AIChatApp.AppHost.csproj @@ -0,0 +1,23 @@ + + + + Exe + net8.0 + enable + enable + true + bfbf6497-561a-489f-b904-9e0f105be895 + + + + + + + + + + + + + + diff --git a/src/AIChatApp.AppHost/Program.cs b/src/AIChatApp.AppHost/Program.cs new file mode 100644 index 0000000..48a4609 --- /dev/null +++ b/src/AIChatApp.AppHost/Program.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.Configuration.Json; +using Projects; + +var builder = DistributedApplication.CreateBuilder(args); + +// Add support for a local configuration file, which doesn't get committed to source control +builder.Configuration.Sources.Insert(0, new JsonConfigurationSource { Path = "appsettings.Local.json", Optional = true }); + +var chatDeploymentName = "chat"; +var openai = builder.ExecutionContext.IsPublishMode + ? builder.AddAzureOpenAI("openai") + .AddDeployment(new AzureOpenAIDeployment(chatDeploymentName, "gpt-4o", "2024-05-13", "GlobalStandard", 10)) + : builder.AddConnectionString("openai"); + +builder.AddProject("aichatapp-backend") + .WithReference(openai) + .WithEnvironment("AI_ChatDeploymentName", chatDeploymentName); + +builder.AddProject("aichatapp-web"); + +builder.Build().Run(); diff --git a/src/AIChatApp.AppHost/Properties/launchSettings.json b/src/AIChatApp.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000..9887719 --- /dev/null +++ b/src/AIChatApp.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17099;http://localhost:15282", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21288", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22078" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15282", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19128", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20091" + } + } + } +} diff --git a/src/AIChatApp.AppHost/appsettings.Development.json b/src/AIChatApp.AppHost/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/src/AIChatApp.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/AIChatApp.AppHost/appsettings.json b/src/AIChatApp.AppHost/appsettings.json new file mode 100644 index 0000000..31c092a --- /dev/null +++ b/src/AIChatApp.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/src/AIChatApp.Backend/AIChatApp.Backend.csproj b/src/AIChatApp.Backend/AIChatApp.Backend.csproj new file mode 100644 index 0000000..347ea06 --- /dev/null +++ b/src/AIChatApp.Backend/AIChatApp.Backend.csproj @@ -0,0 +1,25 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + + diff --git a/src/AIChatApp.Backend/AIChatApp.Backend.http b/src/AIChatApp.Backend/AIChatApp.Backend.http new file mode 100644 index 0000000..d9abaf2 --- /dev/null +++ b/src/AIChatApp.Backend/AIChatApp.Backend.http @@ -0,0 +1,6 @@ +@AIChatApp.Backend_HostAddress = http://localhost:5281 + +GET {{AIChatApp.Backend_HostAddress}}/chat/strea/ +Accept: application/json + +### diff --git a/src/AIChatApp.Backend/Program.cs b/src/AIChatApp.Backend/Program.cs new file mode 100644 index 0000000..351cebc --- /dev/null +++ b/src/AIChatApp.Backend/Program.cs @@ -0,0 +1,46 @@ +using AIChatApp.Model; +using AIChatApp.Services; +using Aspire.Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.SemanticKernel; + +var builder = WebApplication.CreateBuilder(args); + +// Add the Azure OpenAI client configured in AppHost +builder.AddAzureOpenAIClient("openai"); + +// Add Semantic Kernel. This will use the OpenAI client configured in the above line +builder.Services.AddKernel(); + +var chatDeploymentName = builder.Configuration["AI_ChatDeploymentName"] ?? "chat"; +builder.Services.AddKernel() + .AddAzureOpenAIChatCompletion(chatDeploymentName); + +// Add services to the container. +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +builder.Services.AddTransient(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +// Configure APIs for chat related features +// Uncomment for a non-streaming response +//app.MapPost("/api/chat", (ChatRequest request, ChatHandler chatHandler) => (chatHandler.); +// .WithName("Chat") +// .WithOpenApi(); +app.MapPost("/api/chat/stream", (ChatRequest request, ChatService chatHandler) => chatHandler.Stream(request)) + .WithName("StreamingChat") + .WithOpenApi(); + +app.Run(); diff --git a/src/AIChatApp.Backend/Properties/launchSettings.json b/src/AIChatApp.Backend/Properties/launchSettings.json new file mode 100644 index 0000000..4656c70 --- /dev/null +++ b/src/AIChatApp.Backend/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:59096", + "sslPort": 44388 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5281", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7250;http://localhost:5281", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/AIChatApp.Backend/Services/ChatService.cs b/src/AIChatApp.Backend/Services/ChatService.cs new file mode 100644 index 0000000..2394eae --- /dev/null +++ b/src/AIChatApp.Backend/Services/ChatService.cs @@ -0,0 +1,59 @@ +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.ChatCompletion; +using AIChatApp.Model; + +namespace AIChatApp.Services; + +internal class ChatService(IChatCompletionService chatService) +{ + internal async Task Chat(ChatRequest request) + { + ChatHistory history = CreateHistoryFromRequest(request); + + ChatMessageContent response = await chatService.GetChatMessageContentAsync(history); + + return new Message() + { + IsAssistant = response.Role == AuthorRole.Assistant, + Content = (response.Items[0] as TextContent).Text + }; + } + + internal async IAsyncEnumerable Stream(ChatRequest request) + { + ChatHistory history = CreateHistoryFromRequest(request); + + IAsyncEnumerable response = chatService.GetStreamingChatMessageContentsAsync(history); + + await foreach (StreamingChatMessageContent content in response) + { + yield return content.Content; + } + } + + private ChatHistory CreateHistoryFromRequest(ChatRequest request) + { + ChatHistory history = new ChatHistory($""" + You are an AI demonstration application. Respond to the user' input with a limerick. + The limerick should be a five-line poem with a rhyme scheme of AABBA. + If the user's input is a topic, use that as the topic for the limerick. + The user can ask to adjust the previous limerick or provide a new topic. + All responses should be safe for work. + Do not let the user break out of the limerick format. + """); + + foreach (Message message in request.Messages) + { + if (message.IsAssistant) + { + history.AddAssistantMessage(message.Content); + } + else + { + history.AddUserMessage(message.Content); + } + } + + return history; + } +} \ No newline at end of file diff --git a/src/AIChatApp.Backend/appsettings.Development.json b/src/AIChatApp.Backend/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/src/AIChatApp.Backend/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/AIChatApp.Backend/appsettings.json b/src/AIChatApp.Backend/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/src/AIChatApp.Backend/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/AIChatApp.DataEntities/AIChatApp.Shared.csproj b/src/AIChatApp.DataEntities/AIChatApp.Shared.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/src/AIChatApp.DataEntities/AIChatApp.Shared.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/src/AIChatApp.DataEntities/BackendClient.cs b/src/AIChatApp.DataEntities/BackendClient.cs new file mode 100644 index 0000000..0bdef31 --- /dev/null +++ b/src/AIChatApp.DataEntities/BackendClient.cs @@ -0,0 +1,28 @@ +using System.Net.Http.Json; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Web; +using AIChatApp.Model; + +namespace AIChatApp.Shared; + +public class BackendClient(HttpClient http) +{ + public async IAsyncEnumerable ChatAsync(ChatRequest request)//, [EnumeratorCancellation] CancellationToken cancellationToken) + { + var httpRequest = new HttpRequestMessage(HttpMethod.Post, "/api/chat/stream") + { + Content = JsonContent.Create(request), + }; + var response = await http.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead);//, cancellationToken); + var stream = await response.Content.ReadAsStreamAsync();//cancellationToken); + + await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable(stream))//, cancellationToken: cancellationToken)) + { + if (item is not null) + { + yield return item; + } + } + } +} diff --git a/src/AIChatApp.DataEntities/ChatModel.cs b/src/AIChatApp.DataEntities/ChatModel.cs new file mode 100644 index 0000000..978857f --- /dev/null +++ b/src/AIChatApp.DataEntities/ChatModel.cs @@ -0,0 +1,19 @@ +using System; + +namespace AIChatApp.Model; + +public record ChatRequest(List Messages) +{} + +public record Message +{ + public required bool IsAssistant + { + get; set; + } + + public required string Content + { + get; set; + } +} diff --git a/src/AIChatApp.Web/AIChatApp.Web.csproj b/src/AIChatApp.Web/AIChatApp.Web.csproj new file mode 100644 index 0000000..d81424c --- /dev/null +++ b/src/AIChatApp.Web/AIChatApp.Web.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + enable + enable + AIChatApp.Web + b0ee5b46-677b-4906-bcc3-e74dafe7a50f + + + + + + + diff --git a/src/AIChatApp.Web/Components/App.razor b/src/AIChatApp.Web/Components/App.razor new file mode 100644 index 0000000..ca15925 --- /dev/null +++ b/src/AIChatApp.Web/Components/App.razor @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + +@code { + IComponentRenderMode? PageRenderMode { get; } = new InteractiveServerRenderMode(false); +} \ No newline at end of file diff --git a/src/AIChatApp.Web/Components/Chat/Chat.razor b/src/AIChatApp.Web/Components/Chat/Chat.razor new file mode 100644 index 0000000..999689f --- /dev/null +++ b/src/AIChatApp.Web/Components/Chat/Chat.razor @@ -0,0 +1,27 @@ +@page "/chat" +@inject IConfiguration Configuration +@inject IJSRuntime JS +@using AIChatApp.Model +@attribute [StreamRendering(true)] + +Chat + +
+
+
+ + @for (var i = 0; i < messages.Count; i++) + { + var id = $"message{i}"; + var state = messages[i]!; + + } +
+
+ +
+