├── Dotnet ├── .gitattributes ├── .gitignore ├── .nuget │ └── nuget.config ├── .vscode │ ├── extensions.json │ └── launch.json ├── BlazorAI.sln ├── LICENSE ├── README.md ├── SECURITY.md ├── challenges │ ├── Challenge-00.md │ ├── Challenge-01.md │ ├── Challenge-02.md │ ├── Challenge-03.md │ ├── Challenge-04.md │ ├── Challenge-05.md │ ├── Challenge-06.md │ ├── Challenge-07.md │ ├── Challenge-08.md │ ├── Challenge-09.md │ ├── Challenge-10.md │ └── Resources │ │ ├── Lectures.pdf │ │ ├── Supporting Challenges │ │ ├── Challenge-00-DevBox.md │ │ ├── Challenge-02-Reference-App.md │ │ └── Challenge-09-Reference-App.md │ │ └── images │ │ ├── CORS.png │ │ ├── admin-center-settings-icon.png │ │ ├── aoai-studio-button.png │ │ ├── app_full_view.png │ │ ├── aspirehttps.png │ │ ├── ch0203.png │ │ ├── ch02I02.png │ │ ├── ch02i01.png │ │ ├── ch0506.png │ │ ├── ch05i01.png │ │ ├── ch05i02.png │ │ ├── ch05i03.png │ │ ├── ch05i04.png │ │ ├── ch05i05.png │ │ ├── ch09i01.png │ │ ├── ch09i02.png │ │ ├── ch09i03.png │ │ ├── ch09i04.png │ │ ├── ch09i05.png │ │ ├── ch09i08.png │ │ ├── ch09i09.png │ │ ├── ch09i10.png │ │ ├── ch10i01.png │ │ ├── ch10i02.png │ │ ├── dotnet_version.png │ │ ├── filesforchallenges.png │ │ ├── filter-blocklist-term.png │ │ ├── filter-blocklists.png │ │ ├── filter-deployment-list.png │ │ ├── filter-name.png │ │ ├── filter-sliders.png │ │ ├── ksnip_20250213-105122.png │ │ ├── playvisualstudio.png │ │ ├── recommendextensions.png │ │ ├── run_aspire_console.png │ │ └── text-embedding-ada-002.png └── src │ ├── Aspire │ ├── Aspire.AppHost │ │ ├── Aspire.AppHost.csproj │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── appsettings.Development.json │ │ └── appsettings.json │ └── Aspire.ServiceDefaults │ │ ├── Aspire.ServiceDefaults.csproj │ │ └── Extensions.cs │ ├── BlazorAI │ ├── BlazorAI.csproj │ ├── Components │ │ ├── App.razor │ │ ├── Layout │ │ │ ├── MainLayout.razor │ │ │ └── MainLayout.razor.css │ │ ├── Models │ │ │ └── AppState.cs │ │ ├── Pages │ │ │ ├── Chat.razor │ │ │ ├── Chat.razor.cs │ │ │ ├── Error.razor │ │ │ ├── Extensions │ │ │ │ ├── ChatExtensions.cs │ │ │ │ └── MultiAgentExtension.cs │ │ │ ├── MultiAgent.razor │ │ │ ├── MultiAgent.razor.cs │ │ │ └── Theme.razor │ │ ├── Routes.razor │ │ ├── Shared │ │ │ ├── FluentUIRequiredFeatures.razor │ │ │ ├── FooterComponent.razor │ │ │ └── FooterComponent.razor.css │ │ └── _Imports.razor │ ├── Plugins │ │ └── GeocodingPlugin.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── data │ │ └── employee_handbook.pdf │ ├── img │ │ └── admin-center-settings-icon.png │ └── wwwroot │ │ ├── app.css │ │ ├── bootstrap │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ ├── css │ │ ├── bootstrap │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ ├── open-iconic │ │ │ ├── FONT-LICENSE │ │ │ ├── ICON-LICENSE │ │ │ ├── README.md │ │ │ └── font │ │ │ │ ├── css │ │ │ │ └── open-iconic-bootstrap.min.css │ │ │ │ └── fonts │ │ │ │ ├── open-iconic.eot │ │ │ │ ├── open-iconic.otf │ │ │ │ ├── open-iconic.svg │ │ │ │ ├── open-iconic.ttf │ │ │ │ └── open-iconic.woff │ │ └── site.css │ │ └── favicon.png │ ├── WorkItems │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── WorkItems.csproj │ ├── WorkItems.http │ ├── WorkItemsDTO.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ └── data │ │ └── workitems.csv │ └── eShopLite │ ├── .editorconfig │ ├── DataEntities │ ├── DataEntities.csproj │ └── Product.cs │ ├── Products │ ├── .config │ │ └── dotnet-tools.json │ ├── Data │ │ └── ProductDataContext.cs │ ├── Endpoints │ │ └── ProductEndpoints.cs │ ├── Memory │ │ └── AiSearchInitializer.cs │ ├── Migrations │ │ ├── 20250221185401_InitialCreate.Designer.cs │ │ ├── 20250221185401_InitialCreate.cs │ │ └── ContextModelSnapshot.cs │ ├── Models │ │ └── Context.cs │ ├── Products.csproj │ ├── Products.http │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ │ └── images │ │ ├── product1.png │ │ ├── product2.png │ │ ├── product3.png │ │ ├── product4.png │ │ ├── product5.png │ │ ├── product6.png │ │ ├── product7.png │ │ ├── product8.png │ │ └── product9.png │ ├── SearchEntities │ ├── SearchEntities.csproj │ └── SearchResponse.cs │ ├── Store │ ├── Components │ │ ├── App.razor │ │ ├── Layout │ │ │ ├── MainLayout.razor │ │ │ ├── MainLayout.razor.css │ │ │ ├── NavMenu.razor │ │ │ └── NavMenu.razor.css │ │ ├── Pages │ │ │ ├── Home.razor │ │ │ ├── Products.razor │ │ │ └── Search.razor │ │ ├── Routes.razor │ │ └── _Imports.razor │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Services │ │ └── ProductService.cs │ ├── Store.csproj │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ │ ├── app.css │ │ ├── bootstrap │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── favicon.png │ ├── VectorEntities │ ├── ProductVector.cs │ ├── ProductVectorAzureAISearch.cs │ └── VectorEntities.csproj │ ├── eShopAppHost │ ├── .gitignore │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── azure.yaml │ ├── eShopAppHost.csproj │ ├── infra │ │ ├── appInsights │ │ │ └── appInsights.module.bicep │ │ ├── azureaisearch │ │ │ └── azureaisearch.module.bicep │ │ ├── cache.tmpl.yaml │ │ ├── cache │ │ │ └── cache.module.bicep │ │ ├── main.bicep │ │ ├── main.parameters.json │ │ ├── openai │ │ │ └── openai.module.bicep │ │ ├── products.tmpl.yaml │ │ ├── resources.bicep │ │ ├── sql.tmpl.yaml │ │ └── store.tmpl.yaml │ └── next-steps.md │ ├── eShopLite.sln │ └── eShopServiceDefaults │ ├── Extensions.cs │ └── eShopServiceDefaults.csproj ├── Java ├── .gitattributes ├── .gitignore ├── README.md ├── challenges │ ├── Challenge-00.md │ ├── Challenge-01.md │ ├── Challenge-02.md │ ├── Challenge-03.md │ ├── Challenge-04.md │ ├── Challenge-05.md │ └── Resources │ │ ├── Lectures.pdf │ │ ├── Supporting Challenges │ │ ├── Challenge-00-DevBox.md │ │ └── Challenge-02-Reference-App.md │ │ └── images │ │ ├── CORS.png │ │ ├── admin-center-settings-icon.png │ │ ├── aoai-studio-button.png │ │ ├── app_full_view.png │ │ ├── aspirehttps.png │ │ ├── ch0203.png │ │ ├── ch02I02.png │ │ ├── ch02i01.png │ │ ├── ch0506.png │ │ ├── ch05i01.png │ │ ├── ch05i02.png │ │ ├── ch05i03.png │ │ ├── ch05i04.png │ │ ├── ch05i05.png │ │ ├── dotnet_version.png │ │ ├── filesforchallenges.png │ │ ├── filter-blocklist-term.png │ │ ├── filter-blocklists.png │ │ ├── filter-deployment-list.png │ │ ├── filter-name.png │ │ ├── filter-sliders.png │ │ ├── playvisualstudio.png │ │ ├── recommendextensions.png │ │ ├── run_aspire_console.png │ │ └── text-embedding-ada-002.png ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── sk │ │ ├── SpringbootSkChatApplication.java │ │ ├── config │ │ ├── AzureAIConfig.java │ │ ├── CorsConfig.java │ │ └── RestConfig.java │ │ ├── controller │ │ └── AIController.java │ │ ├── kernel │ │ └── kernelUtil.java │ │ ├── model │ │ ├── ChatRequest.java │ │ └── Handbook.java │ │ ├── plugins │ │ ├── AISearchPlugin.java │ │ ├── DateTimePlugin.java │ │ ├── GeocodingPlugin.java │ │ └── WeatherPlugin.java │ │ └── service │ │ └── AIService.java │ └── resources │ ├── application.properties.temp │ └── promptconfig │ └── food │ ├── config.json │ └── skprompt.txt ├── LICENSE ├── Python ├── .gitignore ├── .vscode │ └── launch.json ├── README.md ├── challenges │ ├── Challenge-00.md │ ├── Challenge-01.md │ ├── Challenge-02.md │ ├── Challenge-03.md │ ├── Challenge-04.md │ ├── Challenge-05.md │ ├── Challenge-06.md │ ├── Challenge-07.md │ ├── Challenge-08.md │ └── Resources │ │ ├── Supporting Challenges │ │ └── Challenge-02-Reference-App.md │ │ └── image │ │ ├── ch01img1.png │ │ ├── ch02img1.png │ │ ├── ch02img2.png │ │ ├── ch02img3.png │ │ ├── ch05img0.png │ │ ├── ch05img1.png │ │ ├── ch05img2.png │ │ ├── ch05img3.png │ │ ├── ch05img4.png │ │ ├── ch05img4.png.old │ │ ├── ch05img5.png │ │ ├── ch05img5.png.old │ │ ├── ch05img6.png │ │ ├── ch05img6.png.old │ │ ├── ch05img7.png │ │ ├── ch05img8.png │ │ ├── ch05img8.png.old │ │ ├── ch06img1.png │ │ ├── ch06img2.png │ │ ├── ch06img3.png │ │ ├── ch06img4.png │ │ └── ch06img5.png └── src │ ├── .env_template │ ├── app.py │ ├── chat.py │ ├── data │ ├── employee_handbook.pdf │ └── workitems.csv │ ├── models │ └── employee_handbook_model.py │ ├── multi_agent.py │ ├── plugins │ ├── ai_search_plugin.py │ └── geo_coding_plugin.py │ ├── requirements.txt │ └── workitems │ ├── api.py │ └── data │ └── workitems.csv ├── README.md ├── SECURITY.md └── misc └── finalresult.zip /Dotnet/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Dotnet/.nuget/nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Dotnet/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-dotnettools.csdevkit", 4 | "github.copilot", 5 | "bierner.github-markdown-preview" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /Dotnet/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Aspire.AppHost", 9 | "type": "dotnet", 10 | "request": "launch", 11 | "projectPath": "${workspaceFolder}\\src\\Aspire\\Aspire.AppHost\\Aspire.AppHost.csproj", 12 | "launchConfigurationId": "TargetFramework=;https" 13 | }, 14 | { 15 | "name": "BlazorAI", 16 | "type": "dotnet", 17 | "request": "launch", 18 | "projectPath": "${workspaceFolder}\\src\\BlazorAI\\BlazorAI.csproj", 19 | "launchConfigurationId": "TargetFramework=;https" 20 | }, 21 | { 22 | "name": "WorkItems", 23 | "type": "dotnet", 24 | "request": "launch", 25 | "projectPath": "${workspaceFolder}\\src\\WorkItems\\WorkItems.csproj", 26 | "launchConfigurationId": "TargetFramework=;http" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /Dotnet/BlazorAI.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.002.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorAI", "src\BlazorAI\BlazorAI.csproj", "{3A14576B-09EC-40E8-9440-5063A6EB4394}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.AppHost", "src\Aspire\Aspire.AppHost\Aspire.AppHost.csproj", "{AE2DBA09-2D31-4423-8941-1F7EE00B20CC}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.ServiceDefaults", "src\Aspire\Aspire.ServiceDefaults\Aspire.ServiceDefaults.csproj", "{A6363E5B-3D3B-4055-A3C7-0A66CA612D20}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aspire", "Aspire", "{902AC2ED-059D-49C9-AD62-E14BE6D6976C}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkItems", "src\WorkItems\WorkItems.csproj", "{8DEBEDF9-D232-4693-8A07-B6F39E9A6D4A}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {3A14576B-09EC-40E8-9440-5063A6EB4394}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {3A14576B-09EC-40E8-9440-5063A6EB4394}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {3A14576B-09EC-40E8-9440-5063A6EB4394}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {3A14576B-09EC-40E8-9440-5063A6EB4394}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {AE2DBA09-2D31-4423-8941-1F7EE00B20CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {AE2DBA09-2D31-4423-8941-1F7EE00B20CC}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {AE2DBA09-2D31-4423-8941-1F7EE00B20CC}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {AE2DBA09-2D31-4423-8941-1F7EE00B20CC}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {A6363E5B-3D3B-4055-A3C7-0A66CA612D20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {A6363E5B-3D3B-4055-A3C7-0A66CA612D20}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {A6363E5B-3D3B-4055-A3C7-0A66CA612D20}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {A6363E5B-3D3B-4055-A3C7-0A66CA612D20}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {8DEBEDF9-D232-4693-8A07-B6F39E9A6D4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {8DEBEDF9-D232-4693-8A07-B6F39E9A6D4A}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {8DEBEDF9-D232-4693-8A07-B6F39E9A6D4A}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {8DEBEDF9-D232-4693-8A07-B6F39E9A6D4A}.Release|Any CPU.Build.0 = Release|Any CPU 38 | EndGlobalSection 39 | GlobalSection(SolutionProperties) = preSolution 40 | HideSolutionNode = FALSE 41 | EndGlobalSection 42 | GlobalSection(NestedProjects) = preSolution 43 | {AE2DBA09-2D31-4423-8941-1F7EE00B20CC} = {902AC2ED-059D-49C9-AD62-E14BE6D6976C} 44 | {A6363E5B-3D3B-4055-A3C7-0A66CA612D20} = {902AC2ED-059D-49C9-AD62-E14BE6D6976C} 45 | EndGlobalSection 46 | GlobalSection(ExtensibilityGlobals) = postSolution 47 | SolutionGuid = {9E9489F3-320C-4D31-AA0D-487C0C02DFE1} 48 | EndGlobalSection 49 | EndGlobal 50 | -------------------------------------------------------------------------------- /Dotnet/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Chris McKee 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 | -------------------------------------------------------------------------------- /Dotnet/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 | -------------------------------------------------------------------------------- /Dotnet/challenges/Challenge-10.md: -------------------------------------------------------------------------------- 1 | [< Previous Challenge](./Challenge-09.md) - [**Home**](../README.md) - [Next Challenge >](./Challenge-11.md) 2 | 3 | # Challenge-10 - Enrich user experience with Semantic Kernel 4 | 5 | ## Introduction 6 | 7 | In this challenge, you will further enhance the Product search page by having the AI model generate a creative response to the users query and the products returned from the Semantic Search. a Chat Completion AI model will be used to replace the generic response message when the search completes 8 | 9 | ## Description 10 | 11 | The current search response simply tells the user how many products were found and lists the products. 12 | 13 | ![simple response](./Resources/images/ch10i01.png) 14 | 15 | The goal of this challenge is to replace the current search response with a creative response generated by an AI model. 16 | 17 | ![creative response](./Resources/images/ch10i02.png) 18 | 19 | ## Prerequisites 20 | 21 | 1. Complete the [Getting Familiar With eShop Lite](./Resources/Supporting%20Challenges/Challenge-09-Reference-App.md) guide. 22 | 1. Complete [Challenge 09 - Infuse Apps with AI Using Semantic Kernel](./Challenge-09.md). 23 | 24 | ## Challenges 25 | 26 | 1. Add the Azure OpenAI ChatCompletion model to the **Products.cs** file. 27 | 28 | :pushpin: **Note:** Add the code after the comment `//Challenge 10 - Register a Chat Completion Model` in the **program.cs** file. 29 | 30 | 1. Update the generic response in the **ProductEndpoints.cs** file to use the ChatCompletion model to generate a creative response to the user. 31 | 32 | Replace the following code with a call to a Chat Completion AI model. 33 | 34 | ```csharp 35 | response.Response = products.Count > 0 ? 36 | $"{products.Count} Products found for [{search}]" : 37 | $"No products found for [{search}]"; 38 | ``` 39 | 40 | Construct a prompt that directs the AI model to generate a creative and engaging response for the user. The prompt should emphasize that the user is searching for camping products and should produce a catchy and friendly message incorporating the user's search query and details, including price, of the first product from the search results. 41 | 42 | 43 | ## Success Criteria 44 | 45 | 1. Verify the Chat Completion model was registered correctly. 46 | 1. Verify the AI model generates a response based on the users search query. 47 | 1. Verify the response includes information about the first product in the search results including price. 48 | 49 | --- 50 | 51 | [< Previous Challenge](./Challenge-09.md) - [**Home**](../README.md) - [Next Challenge >](./Challenge-11.md) 52 | -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/Lectures.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/Lectures.pdf -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/Supporting Challenges/Challenge-00-DevBox.md: -------------------------------------------------------------------------------- 1 | ### [< Back to Challenge 0](../../Challenge-00.md) 2 | 3 | ## Why use Azure DevBox for this workshop? 4 | 5 | If you are using a tenant/subscription provided by Microsoft for this workshop then we have created an Azure DevBox just incase you are running into issues on your own computer or your IT department block developer certs or localhost. 6 | 7 | ## What is Azure DevBox? 8 | 9 | Azure DevBox is a cloud-based workstation that gives you a complete developer environment in the cloud. It’s already configured, so you can start coding quickly. However it will take about 25 minutes to deploy. 10 | 11 | ## How to Create Your Dev Box 12 | 13 | 1. Go to [Azure DevBox](https://devbox.microsoft.com/). 14 | 1. Sign in using the credentials given when you registered. 15 | 1. Click to create your Dev Box, no additional configuration is needed. 16 | 17 | ## Logging In 18 | 19 | 1. Once your Dev Box is ready, sign in with your credentials. 20 | 1. You’ll see a Windows environment ready for coding. 21 | 22 | ## Clone the Repository and launch VS Code 23 | 24 | 1. Open a terminal (PowerShell or Command Prompt). 25 | 1. Run: 26 | 27 | ```bash 28 | git clone https://github.com/microsoft/ai-developer.git 29 | ``` 30 | 31 | 1. Change into the cloned directory: 32 | 33 | ```bash 34 | cd ai-developer 35 | ``` 36 | 37 | 1. In the same terminal, enter: 38 | 39 | ```bash 40 | code . 41 | ``` 42 | 43 | 1. When prompted in VS Code to install recommended extensions, choose Yes to get everything you need for development. 44 | 45 | ![install recommended extensions](../images/recommendextensions.png) 46 | 47 | > [!NOTE] 48 | > If you don't see the popup, then just install the recommended extensions. 49 | -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/Supporting Challenges/Challenge-09-Reference-App.md: -------------------------------------------------------------------------------- 1 | ### [< Back to Challenge 09](../../Challenge-09.md) 2 | 3 | # Getting Familiar With eShop Lite 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/CORS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/CORS.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/admin-center-settings-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/admin-center-settings-icon.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/aoai-studio-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/aoai-studio-button.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/app_full_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/app_full_view.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/aspirehttps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/aspirehttps.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch0203.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch0203.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch02I02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch02I02.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch02i01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch02i01.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch0506.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch0506.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch05i01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch05i01.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch05i02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch05i02.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch05i03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch05i03.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch05i04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch05i04.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch05i05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch05i05.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch09i01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch09i01.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch09i02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch09i02.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch09i03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch09i03.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch09i04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch09i04.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch09i05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch09i05.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch09i08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch09i08.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch09i09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch09i09.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch09i10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch09i10.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch10i01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch10i01.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ch10i02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ch10i02.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/dotnet_version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/dotnet_version.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/filesforchallenges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/filesforchallenges.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/filter-blocklist-term.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/filter-blocklist-term.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/filter-blocklists.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/filter-blocklists.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/filter-deployment-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/filter-deployment-list.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/filter-name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/filter-name.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/filter-sliders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/filter-sliders.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/ksnip_20250213-105122.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/ksnip_20250213-105122.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/playvisualstudio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/playvisualstudio.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/recommendextensions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/recommendextensions.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/run_aspire_console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/run_aspire_console.png -------------------------------------------------------------------------------- /Dotnet/challenges/Resources/images/text-embedding-ada-002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/challenges/Resources/images/text-embedding-ada-002.png -------------------------------------------------------------------------------- /Dotnet/src/Aspire/Aspire.AppHost/Aspire.AppHost.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | true 9 | 8a4cc2ea-9b4c-4181-8752-723537805ad6 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Dotnet/src/Aspire/Aspire.AppHost/Program.cs: -------------------------------------------------------------------------------- 1 | var builder = DistributedApplication.CreateBuilder(args); 2 | 3 | var workitems = builder.AddProject("workitems-api"); 4 | 5 | builder.AddProject("blazor-aichat") 6 | .WithExternalHttpEndpoints() 7 | .WithReference(workitems) 8 | .WaitFor(workitems) 9 | .WithEnvironment("WORKITEMS_BASE_URL", workitems.GetEndpoint("https")) 10 | .WithEnvironment("OPEN_API_DOC_ROUTE", "/openapi/v1.json"); 11 | 12 | builder.Build().Run(); 13 | -------------------------------------------------------------------------------- /Dotnet/src/Aspire/Aspire.AppHost/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "https": { 5 | "commandName": "Project", 6 | "dotnetRunMessages": true, 7 | "launchBrowser": true, 8 | "applicationUrl": "https://localhost:17188;http://localhost:15091", 9 | "environmentVariables": { 10 | "ASPNETCORE_ENVIRONMENT": "Development", 11 | "DOTNET_ENVIRONMENT": "Development", 12 | "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21252", 13 | "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22153" 14 | } 15 | }, 16 | "http": { 17 | "commandName": "Project", 18 | "dotnetRunMessages": true, 19 | "launchBrowser": true, 20 | "applicationUrl": "http://localhost:15091", 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development", 23 | "DOTNET_ENVIRONMENT": "Development", 24 | "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19119", 25 | "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20048" 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Dotnet/src/Aspire/Aspire.AppHost/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Dotnet/src/Aspire/Aspire.AppHost/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning", 6 | "Aspire.Hosting.Dcp": "Warning" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Dotnet/src/Aspire/Aspire.ServiceDefaults/Aspire.ServiceDefaults.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | enable 5 | enable 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/BlazorAI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 82f975cb-37d5-41d2-b6cc-a7add5c54c9b 8 | SKEXP0110,SKEXP0010,SKEXP0001 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | PreserveNewest 44 | true 45 | PreserveNewest 46 | 47 | 48 | PreserveNewest 49 | true 50 | PreserveNewest 51 | 52 | 53 | true 54 | 55 | 56 | true 57 | 58 | 59 | true 60 | 61 | 62 | true 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Blazor AI Chat App 6 | 7 | 8 | 9 | 10 | 11 | @* *@ 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | @inject NavigationManager Navigation 3 | 4 | 5 | 6 | 7 | Welcome to the AI Workshop for Developers 8 | 9 | 10 | 11 | 12 | Chat 13 | Multi-Agent 14 | 15 | 16 |
17 | @Body 18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | @code { 30 | private void NavigateToChat() 31 | { 32 | Navigation.NavigateTo("chat"); 33 | } 34 | 35 | private void NavigateToMultiAgent() 36 | { 37 | Navigation.NavigateTo("multiagent"); 38 | } 39 | } -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/Layout/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | /*!* General styles *!*/ 2 | /*html, body {*/ 3 | /* height: 100%;*/ 4 | /* margin: 0;*/ 5 | /* padding: 0;*/ 6 | /*}*/ 7 | 8 | /*.main-layout {*/ 9 | /* display: flex;*/ 10 | /* flex-direction: column;*/ 11 | /* width: 100%;*/ 12 | /* height: 100%;*/ 13 | /* padding: 5px;*/ 14 | /* box-sizing: border-box;*/ 15 | /*}*/ 16 | 17 | /*.header {*/ 18 | /* width: 100%;*/ 19 | /* padding: 5px;*/ 20 | /* box-sizing: border-box;*/ 21 | /* flex-shrink: 0;*/ 22 | /*}*/ 23 | 24 | /*.main-body {*/ 25 | /* width: 100%;*/ 26 | /* padding: 5px;*/ 27 | /* box-sizing: border-box;*/ 28 | /* flex: 1;*/ 29 | /* display: flex;*/ 30 | /* flex-direction: column;*/ 31 | /*}*/ 32 | 33 | /*.content-wrapper {*/ 34 | /* width: 100%;*/ 35 | /* flex: 1;*/ 36 | /* box-sizing: border-box;*/ 37 | /* overflow-y: auto;*/ 38 | /*}*/ 39 | 40 | /*.footer {*/ 41 | /* width: 100%;*/ 42 | /* box-sizing: border-box;*/ 43 | /* flex-shrink: 0;*/ 44 | /* position: sticky;*/ 45 | /* bottom: 0;*/ 46 | /*}*/ 47 | 48 | /*!* Styles for the lists *!*/ 49 | /*ul {*/ 50 | /* list-style-type: disc;*/ 51 | /* margin-left: 15px;*/ 52 | /* padding: 0;*/ 53 | /*}*/ 54 | 55 | /*ol {*/ 56 | /* list-style-type: decimal;*/ 57 | /* margin-left: 15px;*/ 58 | /* padding: 0;*/ 59 | /*}*/ 60 | 61 | /*!* Styles for Fluent components *!*/ 62 | /*.FluentHeader, .FluentBodyContent, .FluentNavLink {*/ 63 | /* margin: 5px 0;*/ 64 | /* padding: 0;*/ 65 | /*}*/ 66 | 67 | /*!* Responsive styles *!*/ 68 | /*@media (max-width: 768px) {*/ 69 | /* .main-layout {*/ 70 | /* padding: 3px;*/ 71 | /* }*/ 72 | 73 | /* .header {*/ 74 | /* padding: 3px;*/ 75 | /* }*/ 76 | 77 | /* .main-body {*/ 78 | /* padding: 3px;*/ 79 | /* }*/ 80 | 81 | /* .footer {*/ 82 | /* padding: 3px;*/ 83 | /* }*/ 84 | 85 | /* .content-wrapper {*/ 86 | /* height: auto;*/ 87 | /* }*/ 88 | 89 | /* .fluent-main-layout {*/ 90 | /* flex-direction: column;*/ 91 | /* }*/ 92 | 93 | /* .fluent-main-layout header,*/ 94 | /* .fluent-main-layout subheader,*/ 95 | /* .fluent-main-layout body,*/ 96 | /* .fluent-main-layout navmenucontent {*/ 97 | /* width: 100%;*/ 98 | /* }*/ 99 | /*}*/ 100 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/Models/AppState.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorAI.Components.Models 2 | { 3 | public class AppState 4 | { 5 | public event Action OnChange; 6 | 7 | public void NotifyStateChanged() => OnChange?.Invoke(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/Pages/Chat.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | @inject HttpClient Http 4 | 5 | 6 | Blazor Chat 7 | 8 | 9 |
10 | 11 | Chat 12 | 13 | New Chat 14 | 15 |
16 |
17 | @if (chatHistory != null && chatHistory.Any()) 18 | { 19 | @foreach (var message in chatHistory) 20 | { 21 | if ((message.Role.ToString().ToLower() == "tool" && !displayToolMessages) || string.IsNullOrEmpty(message.Content)) continue; 22 |
23 |
24 | @if (message.Role.ToString() == "user") 25 | { 26 | 27 | } 28 | else if (message.Role.ToString().ToLower() == "tool" && displayToolMessages) 29 | { 30 | 31 | } 32 | else 33 | { 34 | 35 | } 36 |
37 | @if (!string.IsNullOrEmpty(message.Content)) 38 | { 39 | @((MarkupString)Markdig.Markdown.ToHtml(message.Content, pipeline)) 40 | } 41 |
42 |
43 |
44 | } 45 | } 46 | else 47 | { 48 | No messages yet. Start the conversation! 49 | } 50 | 51 |
52 |
53 | 54 | 57 | Send 60 | 61 |
62 |
63 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/Pages/Chat.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using Microsoft.SemanticKernel; 3 | using Microsoft.SemanticKernel.ChatCompletion; 4 | 5 | #pragma warning disable SKEXP0040 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 6 | #pragma warning disable SKEXP0020 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 7 | #pragma warning disable SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 8 | #pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 9 | 10 | namespace BlazorAI.Components.Pages; 11 | 12 | public partial class Chat 13 | { 14 | private ChatHistory? chatHistory; 15 | private Kernel? kernel; 16 | 17 | [Inject] 18 | public required IConfiguration Configuration { get; set; } 19 | [Inject] 20 | private ILoggerFactory LoggerFactory { get; set; } = null!; 21 | 22 | protected async Task InitializeSemanticKernel() 23 | { 24 | chatHistory = []; 25 | 26 | // Challenge 02 - Configure Semantic Kernel 27 | var kernelBuilder = Kernel.CreateBuilder(); 28 | 29 | // Challenge 02 - Add OpenAI Chat Completion 30 | kernelBuilder.AddAzureOpenAIChatCompletion( 31 | Configuration["AOI_DEPLOYMODEL"]!, 32 | Configuration["AOI_ENDPOINT"]!, 33 | Configuration["AOI_API_KEY"]!); 34 | 35 | // Add Logger for Kernel 36 | kernelBuilder.Services.AddSingleton(LoggerFactory); 37 | 38 | // Challenge 03 and 04 - Services Required 39 | kernelBuilder.Services.AddHttpClient(); 40 | 41 | // Challenge 05 - Register Azure AI Foundry Text Embeddings Generation 42 | 43 | 44 | // Challenge 05 - Register Search Index 45 | 46 | 47 | // Challenge 07 - Add Azure AI Foundry Text To Image 48 | 49 | 50 | // Challenge 02 - Finalize Kernel Builder 51 | kernel = kernelBuilder.Build(); 52 | 53 | // Challenge 03, 04, 05, & 07 - Add Plugins 54 | await AddPlugins(); 55 | 56 | // Challenge 03 - Create OpenAIPromptExecutionSettings 57 | 58 | 59 | } 60 | 61 | 62 | private async Task AddPlugins() 63 | { 64 | // Challenge 03 - Add Time Plugin 65 | 66 | // Challenge 04 - Import OpenAPI Spec 67 | 68 | // Challenge 05 - Add Search Plugin 69 | 70 | // Challenge 07 - Text To Image Plugin 71 | 72 | } 73 | 74 | private async Task SendMessage() 75 | { 76 | if (!string.IsNullOrWhiteSpace(newMessage) && chatHistory != null) 77 | { 78 | // This tells Blazor the UI is going to be updated. 79 | StateHasChanged(); 80 | loading = true; 81 | // Copy the user message to a local variable and clear the newMessage field in the UI 82 | var userMessage = newMessage; 83 | newMessage = string.Empty; 84 | StateHasChanged(); 85 | 86 | // Challenge 02 - Retrieve the chat completion service 87 | 88 | // Challenge 02 - Update Chat History 89 | 90 | // Challenge 02 - Send a message to the chat completion service 91 | 92 | // Challenge 02 - Add Response to the Chat History object 93 | 94 | 95 | loading = false; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/error" 2 | 3 | 4 |

Error.

5 |

An error occurred while processing your request.

6 | 7 |

Development Mode

8 |

9 | Swapping to Development environment will display more detailed information about the error that occurred. 10 |

11 |

12 | The Development environment shouldn't be enabled for deployed applications. 13 | It can result in displaying sensitive information from exceptions to end users. 14 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 15 | and restarting the app. 16 |

17 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/Pages/Extensions/ChatExtensions.cs: -------------------------------------------------------------------------------- 1 | using BlazorAI.Components.Models; 2 | using Markdig; 3 | using Microsoft.AspNetCore.Components; 4 | using Microsoft.FluentUI.AspNetCore.Components; 5 | using Microsoft.JSInterop; 6 | 7 | namespace BlazorAI.Components.Pages; 8 | 9 | #pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 10 | #pragma warning disable SKEXP0050 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 11 | #pragma warning disable SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 12 | #pragma warning disable SKEXP0040 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 13 | #pragma warning disable SKEXP0020 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 14 | 15 | public partial class Chat 16 | { 17 | private const int MIN_ROWS = 2; 18 | private const int MAX_ROWS = 6; 19 | private string newMessage = string.Empty; 20 | private int textRows = 0; 21 | bool loading = false; 22 | private FluentButton submitButton; 23 | private FluentTextArea inputTextArea; 24 | private int messagesLastScroll = 0; 25 | private bool displayToolMessages = false; 26 | 27 | [Inject] 28 | private IKeyCodeService KeyCodeService { get; set; } 29 | 30 | [Inject] 31 | IJSRuntime JsRuntime { get; set; } = null!; 32 | 33 | [Inject] 34 | public AppState AppState { get; set; } = null!; 35 | 36 | 37 | private MarkdownPipeline pipeline = new MarkdownPipelineBuilder() 38 | .UseAdvancedExtensions() 39 | .UseBootstrap() 40 | .UseEmojiAndSmiley() 41 | .Build(); 42 | 43 | protected override async Task OnInitializedAsync() 44 | { 45 | // This is used by Blazor to capture the user input for shortcut keys. 46 | KeyCodeService.Clear(); 47 | KeyCodeService.RegisterListener(OnKeyDownAsync); 48 | // Initialize the chat history here 49 | await InitializeSemanticKernel(); 50 | } 51 | 52 | protected override async Task OnAfterRenderAsync(bool firstRender) 53 | { 54 | await base.OnAfterRenderAsync(firstRender); 55 | await JsRuntime.InvokeVoidAsync("highlightCode"); 56 | 57 | if (!firstRender && chatHistory != null && chatHistory.Any() && messagesLastScroll != chatHistory.Count) 58 | { 59 | messagesLastScroll = chatHistory.Count; 60 | await JsRuntime.InvokeVoidAsync("scrollToBottom"); 61 | } 62 | } 63 | 64 | protected string MessageInput 65 | { 66 | get => newMessage; 67 | set 68 | { 69 | newMessage = value; 70 | CalculateTextRows(value); 71 | } 72 | } 73 | 74 | private void ClearChat() 75 | { 76 | chatHistory?.Clear(); 77 | } 78 | 79 | private void CalculateTextRows(string value) 80 | { 81 | textRows = Math.Max(value.Split('\n').Length, value.Split('\r').Length); 82 | textRows = Math.Max(textRows, MIN_ROWS); 83 | textRows = Math.Min(textRows, MAX_ROWS); 84 | } 85 | 86 | private async Task OnKeyDownAsync(FluentKeyCodeEventArgs args) 87 | { 88 | if (args.CtrlKey && args.Value == "Enter") 89 | { 90 | Console.WriteLine("Ctrl+Enter Pressed"); 91 | await InvokeAsync(async () => 92 | { 93 | StateHasChanged(); 94 | await Task.Delay(180); 95 | Console.WriteLine("Value in TextArea: {0}", MessageInput); 96 | await submitButton.OnClick.InvokeAsync(); 97 | }); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/Pages/MultiAgent.razor: -------------------------------------------------------------------------------- 1 | @page "/multiagent" 2 | 3 | Multi-agent Chat 4 | 5 | 6 |
7 | 8 | Multi-Agent Chat 9 | 10 | New Chat 11 | 12 |
13 |
14 | @if (chatHistory != null && chatHistory.Any()) 15 | { 16 | @foreach (var message in chatHistory) 17 | { 18 | if ((message.Role.ToString().ToLower() == "tool" && !displayToolMessages) || string.IsNullOrEmpty(message.Content)) continue; 19 |
20 |
21 | @if (message.Role.ToString() == "user") 22 | { 23 | 24 | } 25 | else if (message.Role.ToString().ToLower() == "tool" && displayToolMessages) 26 | { 27 | 28 | } 29 | else 30 | { 31 | 32 | } 33 |
34 | @if (!string.IsNullOrEmpty(message.Content)) 35 | { 36 | @message.AuthorName 37 |
38 | @((MarkupString)Markdig.Markdown.ToHtml(message.Content, pipeline)) 39 | } 40 |
41 |
42 |
43 | } 44 | } 45 | else 46 | { 47 | No messages yet. Start the conversation! 48 | } 49 | 50 |
51 |
52 | 53 | 56 | Send 59 | 60 |
61 |
62 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/Pages/Theme.razor: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 12 | 13 | 14 | 19 | 20 | 21 | 24 | @context 25 | 26 | 27 | 28 | Feeling lucky? 29 | 30 | 31 | 32 | @code { 33 | public DesignThemeModes Mode { get; set; } 34 | 35 | public OfficeColor? OfficeColor { get; set; } 36 | 37 | void OnLoaded(LoadedEventArgs e) 38 | { 39 | 40 | } 41 | 42 | void OnLuminanceChanged(LuminanceChangedEventArgs e) 43 | { 44 | StateHasChanged(); 45 | } 46 | 47 | void PickRandomColor() 48 | { 49 | OfficeColor = OfficeColorUtilities.GetRandom(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/Routes.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | @* *@ 9 | 10 | 11 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/Shared/FluentUIRequiredFeatures.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/Shared/FooterComponent.razor: -------------------------------------------------------------------------------- 1 | 2 | Getting Started with Fluent and Blazor 3 | Smart Paste Docs 4 | Smart ComboBox Docs 5 | Smart Text Area Docs 6 | 7 | 8 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/Shared/FooterComponent.razor.css: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Components/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Authorization 3 | @using Microsoft.AspNetCore.Components.Authorization 4 | @using Microsoft.AspNetCore.Components.Forms 5 | @using Microsoft.AspNetCore.Components.Routing 6 | @using Microsoft.AspNetCore.Components.Web 7 | @using Microsoft.JSInterop 8 | @using BlazorAI.Components 9 | @using BlazorAI.Components.Shared 10 | @using Microsoft.FluentUI.AspNetCore.Components 11 | @using Microsoft.FluentUI.AspNetCore.Components.DesignTokens 12 | @using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons 13 | @using BlazorAI.Components.Pages 14 | @using Microsoft.SemanticKernel 15 | @using Microsoft.SemanticKernel.ChatCompletion; 16 | @using Microsoft.SemanticKernel.Connectors.OpenAI; 17 | @using Microsoft.AspNetCore.Components; 18 | @using System.Collections.Generic; 19 | @using System.Threading.Tasks; 20 | @using System; 21 | @using Microsoft.FluentUI.AspNetCore.Components.Extensions 22 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Plugins/GeocodingPlugin.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.SemanticKernel; 2 | using System.ComponentModel; 3 | 4 | namespace BlazorAI.Plugins 5 | { 6 | public class GeocodingPlugin 7 | { 8 | private readonly IHttpClientFactory _httpClientFactory; 9 | private readonly string _apiKey; 10 | 11 | public GeocodingPlugin(IHttpClientFactory httpClientFactory, IConfiguration configuration) 12 | { 13 | _httpClientFactory = httpClientFactory; 14 | _apiKey = configuration["GEOCODING_API_KEY"] ?? throw new MissingFieldException("GEOCODING_API_KEY"); 15 | } 16 | 17 | [KernelFunction("geocode_address")] 18 | [Description("Takes an address search query, and returns a collection of latitude and longitude coordinates that are most likely to match the query. The more specific the query, the better the results. IE: use 27301, USA to get the address of a postal code in the US. Or '5027 Bartley Way, McLeansville NC' will get better results - than just something like '27301' or 'Springfield'.")] 19 | [return: Description("JSON collection containing a collection of lat and lon values for the supplied address that matches.")] 20 | public async Task GeocodeAddressAsync(string address) 21 | { 22 | using HttpClient httpClient = _httpClientFactory.CreateClient(); 23 | var response = await httpClient.GetStringAsync( 24 | $"https://geocode.maps.co/search?q={address}&api_key={_apiKey}"); 25 | return response; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Program.cs: -------------------------------------------------------------------------------- 1 | using BlazorAI.Components; 2 | using BlazorAI.Components.Models; 3 | using Microsoft.FluentUI.AspNetCore.Components; 4 | using LogLevel = Microsoft.Extensions.Logging.LogLevel; 5 | 6 | #pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 7 | #pragma warning disable SKEXP0050 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 8 | #pragma warning disable SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 9 | #pragma warning disable SKEXP0040 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 10 | #pragma warning disable SKEXP0020 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. 11 | 12 | var builder = WebApplication.CreateBuilder(args); 13 | 14 | builder.Services.AddLogging(loggingBuilder => 15 | { 16 | loggingBuilder.AddConsole(); 17 | loggingBuilder.AddDebug(); 18 | loggingBuilder.AddFilter("Microsoft", LogLevel.Error); 19 | loggingBuilder.AddFilter("System", LogLevel.Error); 20 | loggingBuilder.AddFilter("Microsoft.Identity", LogLevel.Error); 21 | }); 22 | 23 | // Add service defaults & Aspire components. 24 | builder.AddServiceDefaults(); 25 | 26 | builder.Services.AddRazorPages(); 27 | builder.Services.AddRazorComponents() 28 | .AddInteractiveServerComponents(); 29 | 30 | builder.Services.AddControllersWithViews(); 31 | 32 | builder.Services.AddSignalR(); 33 | builder.Services.AddFluentUIComponents(); 34 | 35 | builder.Services.AddScoped(); 36 | builder.Services.AddHttpClient(); 37 | 38 | var app = builder.Build(); 39 | 40 | if (app.Environment.IsDevelopment()) 41 | { 42 | app.UseDeveloperExceptionPage(); 43 | } 44 | else 45 | { 46 | app.UseExceptionHandler("/Error"); 47 | app.UseHsts(); 48 | } 49 | 50 | app.UseHttpsRedirection(); 51 | app.UseStaticFiles(); 52 | 53 | app.UseRouting(); 54 | app.UseAntiforgery(); 55 | app.MapBlazorHub().WithOrder(-1); 56 | 57 | app.MapDefaultEndpoints(); 58 | 59 | app.MapRazorComponents().AddInteractiveServerRenderMode(); 60 | 61 | app.MapControllers(); 62 | 63 | app.Run(); 64 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:62985", 8 | "sslPort": 44377 9 | } 10 | }, 11 | "profiles": { 12 | "https": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "applicationUrl": "https://localhost:7118", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "http": { 22 | "commandName": "Project", 23 | "dotnetRunMessages": true, 24 | "launchBrowser": true, 25 | "applicationUrl": "http://localhost:5154", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | }, 30 | "IIS Express": { 31 | "commandName": "IISExpress", 32 | "launchBrowser": true, 33 | "environmentVariables": { 34 | "ASPNETCORE_ENVIRONMENT": "Development" 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DetailedErrors": true, 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft": "Warning", 7 | "Microsoft.Hosting.Lifetime": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Microsoft.SemanticKernel.Plugins.Web.Bing": "Debug", 5 | "AOAI_Workshop": "Debug", 6 | "Default": "Error", 7 | "System.Net.Http.HttpClient": "Error", 8 | "Microsoft.AspNetCore.Hosting": "Error", 9 | "Microsoft.Hosting.Lifetime": "Information", 10 | "System.Net": "Error", 11 | "Microsoft.Identity.*": "None" 12 | } 13 | }, 14 | "AllowedHosts": "*", 15 | "AOI_DEPLOYMODEL": "Replace with your Azure AI Foundry Deployment model", 16 | "AOI_ENDPOINT": "Replace witu your Azure AI Foundry Enpoint. STOP at the .com , Example - https://aidev.openai.azure.com", 17 | "AOI_API_KEY": "Replace with your API Key", 18 | "EMBEDDINGS_DEPLOYMODEL": "Replace with the deployment name of your Embeddings model", 19 | "DALLE_DEPLOYMODEL": "Replace with your DALL-E model", 20 | "GEOCODING_API_KEY": "Replace with your Geocoding API Key", 21 | "AI_SEARCH_URL": "Replace with your AI Search URI", 22 | "AI_SEARCH_KEY": "Replace with your AI Search API Key" 23 | } 24 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/data/employee_handbook.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/BlazorAI/data/employee_handbook.pdf -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/img/admin-center-settings-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/BlazorAI/img/admin-center-settings-icon.png -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/BlazorAI/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/BlazorAI/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/BlazorAI/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/BlazorAI/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 100%; 3 | height: 100%; 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | } 8 | 9 | body { 10 | display: flex; 11 | flex-direction: column; 12 | min-height: 100vh; 13 | width: 100vw; /* Ensures full width of the viewport */ 14 | } 15 | -------------------------------------------------------------------------------- /Dotnet/src/BlazorAI/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/BlazorAI/wwwroot/favicon.png -------------------------------------------------------------------------------- /Dotnet/src/WorkItems/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:22368", 8 | "sslPort": 44333 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "scalar/v1", 17 | "applicationUrl": "http://localhost:5115", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "launchUrl": "scalar/v1", 27 | "applicationUrl": "https://localhost:7115;http://localhost:5115", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "launchUrl": "scalar/v1", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Dotnet/src/WorkItems/WorkItems.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | PreserveNewest 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Dotnet/src/WorkItems/WorkItems.http: -------------------------------------------------------------------------------- 1 | @WorkItems_HostAddress = http://localhost:5115 2 | 3 | GET {{WorkItems_HostAddress}}/workitems/ 4 | Accept: application/json 5 | 6 | ### 7 | GET {{WorkItems_HostAddress}}/myworkitems/ 8 | Accept: application/json 9 | -------------------------------------------------------------------------------- /Dotnet/src/WorkItems/WorkItemsDTO.cs: -------------------------------------------------------------------------------- 1 | namespace WorkItems; 2 | 3 | public class WorkItemsDTO 4 | { 5 | public int ID { get; set; } 6 | public required string WorkItemType { get; set; } = string.Empty; 7 | public required string Title { get; set; } = string.Empty; 8 | public required string AssignedTo { get; set; } = string.Empty; 9 | public required string State { get; set; } = string.Empty; 10 | public string Tags { get; set; } = string.Empty; 11 | } 12 | -------------------------------------------------------------------------------- /Dotnet/src/WorkItems/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Dotnet/src/WorkItems/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CS8602: Dereference of a possibly null reference. 4 | dotnet_diagnostic.CS8602.severity = silent 5 | 6 | # IDE0058: Expression value is never used 7 | dotnet_diagnostic.IDE0058.severity = silent 8 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/DataEntities/DataEntities.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net9.0 4 | enable 5 | enable 6 | 7 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/DataEntities/Product.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace DataEntities; 4 | 5 | public class Product 6 | { 7 | public Product() 8 | { 9 | Id = 0; 10 | Name = "not defined"; 11 | Description = "not defined"; 12 | Price = 0; 13 | ImageUrl = "not defined"; 14 | } 15 | 16 | [JsonPropertyName("id")] 17 | public virtual int Id { get; set; } 18 | 19 | [JsonPropertyName("name")] 20 | public virtual string Name { get; set; } 21 | 22 | [JsonPropertyName("description")] 23 | public virtual string Description { get; set; } 24 | 25 | [JsonPropertyName("price")] 26 | public virtual decimal Price { get; set; } 27 | 28 | [JsonPropertyName("imageUrl")] 29 | public virtual string ImageUrl { get; set; } 30 | } 31 | 32 | 33 | [JsonSerializable(typeof(List))] 34 | public sealed partial class ProductSerializerContext : JsonSerializerContext 35 | { 36 | } -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-ef": { 6 | "version": "8.0.0-rc.1.23419.6", 7 | "commands": [ 8 | "dotnet-ef" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/Data/ProductDataContext.cs: -------------------------------------------------------------------------------- 1 | using DataEntities; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace Products.Data; 5 | 6 | public class ProductDataContext : DbContext 7 | { 8 | public ProductDataContext(DbContextOptions options) 9 | : base(options) 10 | { 11 | } 12 | 13 | public DbSet Product { get; set; } = default!; 14 | } 15 | 16 | public static class Extensions 17 | { 18 | public static void CreateDbIfNotExists(this IHost host) 19 | { 20 | using var scope = host.Services.CreateScope(); 21 | 22 | var services = scope.ServiceProvider; 23 | var context = services.GetRequiredService(); 24 | context.Database.EnsureCreated(); 25 | DbInitializer.Initialize(context); 26 | } 27 | } 28 | 29 | 30 | public static class DbInitializer 31 | { 32 | public static void Initialize(ProductDataContext context) 33 | { 34 | if (context.Product.Any()) 35 | return; 36 | 37 | var products = new List 38 | { 39 | new Product { Name = "Solar Powered Flashlight", Description = "A fantastic product for outdoor enthusiasts", Price = 19.99m, ImageUrl = "product1.png" }, 40 | new Product { Name = "Hiking Poles", Description = "Ideal for camping and hiking trips", Price = 24.99m, ImageUrl = "product2.png" }, 41 | new Product { Name = "Outdoor Rain Jacket", Description = "This product will keep you warm and dry in all weathers", Price = 49.99m, ImageUrl = "product3.png" }, 42 | new Product { Name = "Survival Kit", Description = "A must-have for any outdoor adventurer", Price = 99.99m, ImageUrl = "product4.png" }, 43 | new Product { Name = "Outdoor Backpack", Description = "This backpack is perfect for carrying all your outdoor essentials", Price = 39.99m, ImageUrl = "product5.png" }, 44 | new Product { Name = "Camping Cookware", Description = "This cookware set is ideal for cooking outdoors", Price = 29.99m, ImageUrl = "product6.png" }, 45 | new Product { Name = "Camping Stove", Description = "This stove is perfect for cooking outdoors", Price = 49.99m, ImageUrl = "product7.png" }, 46 | new Product { Name = "Camping Lantern", Description = "This lantern is perfect for lighting up your campsite", Price = 19.99m, ImageUrl = "product8.png" }, 47 | new Product { Name = "Camping Tent", Description = "This tent is perfect for camping trips", Price = 99.99m, ImageUrl = "product9.png" }, 48 | }; 49 | 50 | context.AddRange(products); 51 | 52 | // sample add 500 products 53 | // context.AddRange(GetProductsToAdd(500, products)); 54 | 55 | context.SaveChanges(); 56 | } 57 | 58 | private static List GetProductsToAdd(int count, List baseProducts) 59 | { 60 | var productsToAdd = new List(); 61 | for (int i = 1; i < count; i++) 62 | { 63 | foreach (var product in baseProducts) 64 | { 65 | var newproduct = new Product 66 | { 67 | Name = $"{product.Name}-{i}", 68 | Description = product.Description, 69 | ImageUrl = product.ImageUrl, 70 | Price = product.Price 71 | }; 72 | productsToAdd.Add(newproduct); 73 | } 74 | } 75 | return productsToAdd; 76 | } 77 | } -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/Memory/AiSearchInitializer.cs: -------------------------------------------------------------------------------- 1 | using Azure.Search.Documents.Indexes; 2 | using Microsoft.Extensions.VectorData; 3 | using Microsoft.SemanticKernel.Connectors.AzureAISearch; 4 | using Microsoft.SemanticKernel.Embeddings; 5 | using Products.Models; 6 | using VectorEntities; 7 | 8 | #pragma warning disable SKEXP0001 9 | 10 | 11 | namespace Products.Memory; 12 | 13 | public class AiSearchInitializer 14 | { 15 | private ILogger _logger; 16 | public ITextEmbeddingGenerationService? _embeddingClient; 17 | public SearchIndexClient? _azureSearchIndexClient; 18 | public IVectorStoreRecordCollection _productsCollection; 19 | 20 | public AiSearchInitializer(ILogger logger, ITextEmbeddingGenerationService? textEmbeddingGenerationService, SearchIndexClient? azureSearchIndexClient) 21 | { 22 | _logger = logger; 23 | _embeddingClient = textEmbeddingGenerationService; 24 | _azureSearchIndexClient = azureSearchIndexClient; 25 | 26 | _logger.LogInformation("Memory context created"); 27 | _logger.LogInformation($"Embedding Client is null: {_embeddingClient is null}"); 28 | _logger.LogInformation($"Azure Search Index Client is null: {_azureSearchIndexClient is null}"); 29 | } 30 | 31 | public async Task InitAiSearchAsync(Context db) 32 | { 33 | _logger.LogInformation("Initializing memory context with AI Search Vector Store"); 34 | if (_embeddingClient is null) { 35 | _logger.LogError("Embedding client is null"); 36 | return false; 37 | } 38 | if (_azureSearchIndexClient is null) { 39 | _logger.LogError("Azure Search Index client is null"); 40 | return false; 41 | } 42 | var vectorProductStore = new AzureAISearchVectorStore(_azureSearchIndexClient); 43 | _productsCollection = vectorProductStore.GetCollection("products"); 44 | await _productsCollection.CreateCollectionIfNotExistsAsync(); 45 | 46 | _logger.LogInformation("Get a copy of the list of products"); 47 | // iterate over the products and add them to the memory 48 | foreach (var product in db.GetAll()) { 49 | try { 50 | _logger.LogInformation("Adding product to AI Search: {Product}", product.Name); 51 | var productInfo = $"[{product.Name}] is a product that costs [{product.Price}] and is described as [{product.Description}]"; 52 | 53 | // new product vector 54 | var productVector = new ProductVectorAzureAISearch { 55 | Id = product.Id.ToString(), 56 | Name = product.Name, 57 | Description = product.Description, 58 | Price = product.Price.ToString(), 59 | ImageUrl = product.ImageUrl, 60 | Vector = await _embeddingClient.GenerateEmbeddingAsync(productInfo) 61 | }; 62 | var recordId = await _productsCollection.UpsertAsync(productVector); 63 | _logger.LogInformation("Product added to memory: {Product} with recordId: {RecordId}", product.Name, recordId); 64 | } catch (Exception exc) { 65 | _logger.LogError(exc, "Error adding product to memory"); 66 | } 67 | } 68 | 69 | _logger.LogInformation("DONE! Updating AI Search"); 70 | return true; 71 | } 72 | 73 | 74 | } -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/Migrations/20250221185401_InitialCreate.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | #pragma warning disable CA1814 // Prefer jagged arrays over multidimensional 6 | 7 | namespace Products.Migrations 8 | { 9 | /// 10 | public partial class InitialCreate : Migration 11 | { 12 | /// 13 | protected override void Up(MigrationBuilder migrationBuilder) 14 | { 15 | migrationBuilder.CreateTable( 16 | name: "Products", 17 | columns: table => new 18 | { 19 | Id = table.Column(type: "INTEGER", nullable: false) 20 | .Annotation("Sqlite:Autoincrement", true), 21 | Name = table.Column(type: "TEXT", nullable: false), 22 | Description = table.Column(type: "TEXT", nullable: false), 23 | Price = table.Column(type: "TEXT", nullable: false), 24 | ImageUrl = table.Column(type: "TEXT", nullable: false) 25 | }, 26 | constraints: table => 27 | { 28 | table.PrimaryKey("PK_Products", x => x.Id); 29 | }); 30 | 31 | migrationBuilder.InsertData( 32 | table: "Products", 33 | columns: new[] { "Id", "Description", "ImageUrl", "Name", "Price" }, 34 | values: new object[,] 35 | { 36 | { 1, "An innovative flashlight that harnesses solar energy, providing reliable illumination for outdoor enthusiasts in any environment.", "product1.png", "Solar Powered Flashlight", 19.99m }, 37 | { 2, "High-quality hiking poles designed to offer stability and support on challenging terrains, making them ideal for camping and hiking trips.", "product2.png", "Hiking Poles", 24.99m }, 38 | { 3, "A premium rain jacket engineered to keep you warm and dry in all weather conditions, ensuring maximum comfort during outdoor activities.", "product3.png", "Outdoor Rain Jacket", 49.99m }, 39 | { 4, "A comprehensive survival kit equipped with essential tools and supplies, making it a must-have for any outdoor adventurer.", "product4.png", "Survival Kit", 99.99m }, 40 | { 5, "A durable and spacious backpack designed to carry all your outdoor essentials, perfect for hiking, camping, and other adventures.", "product5.png", "Outdoor Backpack", 39.99m }, 41 | { 6, "A versatile cookware set specifically designed for outdoor cooking, providing convenience and efficiency for campers.", "product6.png", "Camping Cookware", 29.99m }, 42 | { 7, "A portable camping stove that offers reliable performance, making it an essential item for cooking outdoors.", "product7.png", "Camping Stove", 49.99m }, 43 | { 8, "A high-performance lantern that provides bright and consistent lighting, perfect for illuminating your campsite.", "product8.png", "Camping Lantern", 19.99m }, 44 | { 9, "A robust and spacious tent designed to provide comfort and protection during camping trips, ensuring a pleasant outdoor experience.", "product9.png", "Camping Tent", 99.99m } 45 | }); 46 | } 47 | 48 | /// 49 | protected override void Down(MigrationBuilder migrationBuilder) 50 | { 51 | migrationBuilder.DropTable( 52 | name: "Products"); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/Products.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net9.0 4 | enable 5 | enable 6 | false 7 | 82f975cb-37d5-41d2-b6cc-a7add5c54c9b 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | runtime; build; native; contentfiles; analyzers; buildtransitive 37 | all 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/Products.http: -------------------------------------------------------------------------------- 1 | @HostAddress = http://localhost:5228 2 | 3 | GET {{HostAddress}}/api/Product 4 | Accept: application/json 5 | 6 | ### 7 | 8 | GET {{HostAddress}}/api/Product 9 | Accept: application/json 10 | 11 | ### 12 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/Program.cs: -------------------------------------------------------------------------------- 1 | using Azure; 2 | using Azure.Search.Documents.Indexes; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.SemanticKernel; 5 | using Microsoft.SemanticKernel.Embeddings; 6 | using Products.Endpoints; 7 | using Products.Memory; 8 | using Products.Models; 9 | 10 | #pragma warning disable SKEXP0010 11 | #pragma warning disable SKEXP0001 12 | 13 | var builder = WebApplication.CreateBuilder(args); 14 | 15 | // Disable Globalization Invariant Mode 16 | Environment.SetEnvironmentVariable("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "false"); 17 | 18 | // add aspire service defaults 19 | builder.AddServiceDefaults(); 20 | builder.Services.AddProblemDetails(); 21 | 22 | // Add DbContext service 23 | // Add DbContext service 24 | builder.Services.AddDbContext(options => 25 | options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection"))); 26 | 27 | 28 | builder.Services.AddSingleton(sp => { 29 | return builder.Configuration; 30 | }); 31 | 32 | 33 | //Challenge 9 - Configure the Kernel in an ASP.NET Core app: 34 | 35 | 36 | //Challenge 9 - Register the Azure AI Embedding model 37 | 38 | 39 | //Challenge 9 - Register the Azure AI Search Resource 40 | 41 | 42 | //Challenge 10 - Register a Chat Completion Model 43 | 44 | 45 | //Add AI Search initializer 46 | builder.Services.AddSingleton(sp => { 47 | var logger = sp.GetService>(); 48 | return new AiSearchInitializer(logger!, sp.GetService()!, sp.GetService()); 49 | }); 50 | 51 | // Add services to the container. 52 | var app = builder.Build(); 53 | 54 | // Apply migrations at startup 55 | using (var scope = app.Services.CreateScope()) { 56 | var dbContext = scope.ServiceProvider.GetRequiredService(); 57 | dbContext.Database.Migrate(); 58 | } 59 | 60 | // aspire map default endpoints 61 | app.MapDefaultEndpoints(); 62 | 63 | // Configure the HTTP request pipeline. 64 | app.UseHttpsRedirection(); 65 | 66 | app.MapProductEndpoints(); 67 | 68 | app.UseStaticFiles(); 69 | 70 | 71 | app.Run(); -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:49179", 8 | "sslPort": 44375 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "api/Product", 17 | "applicationUrl": "http://localhost:5228", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "launchUrl": "api/Product", 27 | "applicationUrl": "https://localhost:7130;http://localhost:5228", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "launchUrl": "api/Product", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "ConnectionStrings": { 9 | "DefaultConnection": "Data Source=products.db" 10 | }, 11 | "AllowedHosts": "*", 12 | "AI_SEARCH_URL": "Replace with your AI Search URI", 13 | "AI_SEARCH_KEY": "Replace with your AI Search API Key", 14 | "AOI_DEPLOYMODEL": "Replace with your Azure Open AI Deployment model", 15 | "AOI_ENDPOINT": "Replace with your Azure Open AI Enpoint. Example - https://aidev.openai.azure.com", 16 | "AOI_API_KEY": "Replace with your Azure OpenAI API Key" 17 | } -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/wwwroot/images/product1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/eShopLite/Products/wwwroot/images/product1.png -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/wwwroot/images/product2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/eShopLite/Products/wwwroot/images/product2.png -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/wwwroot/images/product3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/eShopLite/Products/wwwroot/images/product3.png -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/wwwroot/images/product4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/eShopLite/Products/wwwroot/images/product4.png -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/wwwroot/images/product5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/eShopLite/Products/wwwroot/images/product5.png -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/wwwroot/images/product6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/eShopLite/Products/wwwroot/images/product6.png -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/wwwroot/images/product7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/eShopLite/Products/wwwroot/images/product7.png -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/wwwroot/images/product8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/eShopLite/Products/wwwroot/images/product8.png -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Products/wwwroot/images/product9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/eShopLite/Products/wwwroot/images/product9.png -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/SearchEntities/SearchEntities.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | net9.0 7 | enable 8 | enable 9 | 10 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/SearchEntities/SearchResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace SearchEntities; 4 | 5 | public class SearchResponse 6 | { 7 | [JsonPropertyName("id")] 8 | public string? Response { get; set; } 9 | 10 | [JsonPropertyName("products")] 11 | public List? Products { get; set; } 12 | 13 | [JsonPropertyName("elapsedtime")] 14 | public TimeSpan ElapsedTime { get; set; } 15 | } 16 | 17 | 18 | [JsonSerializable(typeof(SearchResponse))] 19 | public sealed partial class SearchResponseSerializerContext : JsonSerializerContext 20 | { 21 | } -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Components/App.razor: -------------------------------------------------------------------------------- 1 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Components/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | Source Code 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | 19 |
20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 |
24 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Components/Layout/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row:not(.auth) { 41 | display: none; 42 | } 43 | 44 | .top-row.auth { 45 | justify-content: space-between; 46 | } 47 | 48 | .top-row ::deep a, .top-row ::deep .btn-link { 49 | margin-left: 0; 50 | } 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .page { 55 | flex-direction: row; 56 | } 57 | 58 | .sidebar { 59 | width: 250px; 60 | height: 100vh; 61 | position: sticky; 62 | top: 0; 63 | } 64 | 65 | .top-row { 66 | position: sticky; 67 | top: 0; 68 | z-index: 1; 69 | } 70 | 71 | .top-row.auth ::deep a:first-child { 72 | flex: 1; 73 | text-align: right; 74 | width: 0; 75 | } 76 | 77 | .top-row, article { 78 | padding-left: 2rem !important; 79 | padding-right: 1.5rem !important; 80 | } 81 | } 82 | 83 | #blazor-error-ui { 84 | background: lightyellow; 85 | bottom: 0; 86 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 87 | display: none; 88 | left: 0; 89 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 90 | position: fixed; 91 | width: 100%; 92 | z-index: 1000; 93 | } 94 | 95 | #blazor-error-ui .dismiss { 96 | cursor: pointer; 97 | position: absolute; 98 | right: 0.75rem; 99 | top: 0.5rem; 100 | } 101 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Components/Layout/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 31 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Components/Layout/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | appearance: none; 3 | cursor: pointer; 4 | width: 3.5rem; 5 | height: 2.5rem; 6 | color: white; 7 | position: absolute; 8 | top: 0.5rem; 9 | right: 1rem; 10 | border: 1px solid rgba(255, 255, 255, 0.1); 11 | background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1); 12 | } 13 | 14 | .navbar-toggler:checked { 15 | background-color: rgba(255, 255, 255, 0.5); 16 | } 17 | 18 | .top-row { 19 | height: 3.5rem; 20 | background-color: rgba(0,0,0,0.4); 21 | } 22 | 23 | .navbar-brand { 24 | font-size: 1.1rem; 25 | } 26 | 27 | .bi { 28 | display: inline-block; 29 | position: relative; 30 | width: 1.25rem; 31 | height: 1.25rem; 32 | margin-right: 0.75rem; 33 | top: -1px; 34 | background-size: cover; 35 | } 36 | 37 | .bi-house-door-fill { 38 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); 39 | } 40 | 41 | .bi-plus-square-fill { 42 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); 43 | } 44 | 45 | .bi-list-nested { 46 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); 47 | } 48 | 49 | .nav-item { 50 | font-size: 0.9rem; 51 | padding-bottom: 0.5rem; 52 | } 53 | 54 | .nav-item:first-of-type { 55 | padding-top: 1rem; 56 | } 57 | 58 | .nav-item:last-of-type { 59 | padding-bottom: 1rem; 60 | } 61 | 62 | .nav-item ::deep a { 63 | color: #d7d7d7; 64 | border-radius: 4px; 65 | height: 3rem; 66 | display: flex; 67 | align-items: center; 68 | line-height: 3rem; 69 | } 70 | 71 | .nav-item ::deep a.active { 72 | background-color: rgba(255,255,255,0.25); 73 | color: white; 74 | } 75 | 76 | .nav-item ::deep a:hover { 77 | background-color: rgba(255,255,255,0.1); 78 | color: white; 79 | } 80 | 81 | .nav-scrollable { 82 | display: none; 83 | } 84 | 85 | .navbar-toggler:checked ~ .nav-scrollable { 86 | display: block; 87 | } 88 | 89 | @media (min-width: 641px) { 90 | .navbar-toggler { 91 | display: none; 92 | } 93 | 94 | .nav-scrollable { 95 | /* Never collapse the sidebar for wide screens */ 96 | display: block; 97 | 98 | /* Allow sidebar to scroll for tall menus */ 99 | height: calc(100vh - 3.5rem); 100 | overflow-y: auto; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Components/Pages/Home.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Home 4 | 5 |

Home

6 | 7 | Welcome to the best e-commerce platform in the world - eShopLite! 8 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Components/Pages/Products.razor: -------------------------------------------------------------------------------- 1 | @page "/products" 2 | @using DataEntities 3 | @using Store.Services 4 | @inject ProductService ProductService 5 | @inject IConfiguration Configuration 6 | @attribute [StreamRendering(true)] 7 | 8 | Products 9 | 10 |

Products

11 | 12 |

Here are some of our amazing outdoor products that you can purchase.

13 | 14 | @if (products == null) 15 | { 16 |

Loading...

17 | } 18 | else 19 | { 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | @foreach (var product in products) 31 | { 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | } 40 | 41 |
ImageNameDescriptionPrice
@product.Name@product.Description@product.Price
42 | } 43 | 44 | @code { 45 | private List? products; 46 | 47 | protected override async Task OnInitializedAsync() 48 | { 49 | // Simulate asynchronous loading to demonstrate streaming rendering 50 | await Task.Delay(500); 51 | products = await ProductService.GetProducts(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Components/Pages/Search.razor: -------------------------------------------------------------------------------- 1 | @page "/search" 2 | 3 | @inject Store.Services.ProductService ProductService 4 | @inject IConfiguration Configuration 5 | @attribute [StreamRendering(true)] 6 | @rendermode InteractiveServer 7 | 8 | Search Products 9 | 10 |

Search Products

11 | 12 |

Search our amazing outdoor products that you can purchase.

13 | 14 |
15 | 16 |
17 | 18 | 19 |
20 |
21 |

@aiResponse

22 | 23 | @* Uncomment this to show elapsed time *@ 24 | @*

Elapsed Time: [@elapsedTime]

*@ 25 |
26 | 27 | 28 | @if (products == null) 29 | { 30 |

Loading...

31 | } 32 | else 33 | { 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | @foreach (var product in products) 45 | { 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | } 54 | 55 |
ImageNameDescriptionPrice
@product.Name@product.Description@product.Price
56 | } 57 | 58 | @code { 59 | private string searchTerm = ""; 60 | private List? products; 61 | private string aiResponse = ""; 62 | private string elapsedTime = ""; 63 | private bool smartSearch = false; 64 | 65 | private async Task DoSearch(MouseEventArgs e) 66 | { 67 | await Task.Delay(500); 68 | var response = await ProductService.Search(searchTerm); 69 | aiResponse = response.Response; 70 | products = response.Products; 71 | elapsedTime = response.ElapsedTime.ToString(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Components/Routes.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Components/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 8 | @using Microsoft.JSInterop 9 | @using Store 10 | @using Store.Components 11 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Program.cs: -------------------------------------------------------------------------------- 1 | using Store.Components; 2 | using Store.Services; 3 | 4 | var builder = WebApplication.CreateBuilder(args); 5 | 6 | // add aspire service defaults 7 | builder.AddServiceDefaults(); 8 | 9 | builder.Services.AddSingleton(); 10 | builder.Services.AddHttpClient( 11 | static client => client.BaseAddress = new("https+http://products")); 12 | 13 | // Add services to the container. 14 | builder.Services.AddRazorComponents() 15 | .AddInteractiveServerComponents(); 16 | 17 | var app = builder.Build(); 18 | 19 | // aspire map default endpoints 20 | app.MapDefaultEndpoints(); 21 | 22 | // Configure the HTTP request pipeline. 23 | if (!app.Environment.IsDevelopment()) 24 | { 25 | app.UseExceptionHandler("/Error", createScopeForErrors: true); 26 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 27 | app.UseHsts(); 28 | } 29 | 30 | app.UseHttpsRedirection(); 31 | 32 | app.UseStaticFiles(); 33 | app.UseAntiforgery(); 34 | 35 | app.MapRazorComponents() 36 | .AddInteractiveServerRenderMode(); 37 | 38 | app.Run(); 39 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:38240", 8 | "sslPort": 44332 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "applicationUrl": "http://localhost:5158", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "https": { 22 | "commandName": "Project", 23 | "dotnetRunMessages": true, 24 | "launchBrowser": true, 25 | "applicationUrl": "https://localhost:7085;http://localhost:5158", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | }, 30 | "IIS Express": { 31 | "commandName": "IISExpress", 32 | "launchBrowser": true, 33 | "environmentVariables": { 34 | "ASPNETCORE_ENVIRONMENT": "Development" 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Services/ProductService.cs: -------------------------------------------------------------------------------- 1 | using SearchEntities; 2 | using DataEntities; 3 | using System.Text.Json; 4 | 5 | namespace Store.Services; 6 | 7 | public class ProductService 8 | { 9 | HttpClient httpClient; 10 | private readonly ILogger _logger; 11 | 12 | public ProductService(HttpClient httpClient, ILogger logger) 13 | { 14 | _logger = logger; 15 | this.httpClient = httpClient; 16 | } 17 | public async Task> GetProducts() 18 | { 19 | List? products = null; 20 | try { 21 | var response = await httpClient.GetAsync("/api/product"); 22 | var responseText = await response.Content.ReadAsStringAsync(); 23 | 24 | _logger.LogInformation($"Http status code: {response.StatusCode}"); 25 | _logger.LogInformation($"Http response content: {responseText}"); 26 | 27 | if (response.IsSuccessStatusCode) { 28 | var options = new JsonSerializerOptions { 29 | PropertyNameCaseInsensitive = true 30 | }; 31 | 32 | products = await response.Content.ReadFromJsonAsync(ProductSerializerContext.Default.ListProduct); 33 | } 34 | } catch (Exception ex) { 35 | _logger.LogError(ex, "Error during GetProducts."); 36 | } 37 | 38 | return products ?? new List(); 39 | } 40 | 41 | public async Task Search(string searchTerm) 42 | { 43 | try { 44 | 45 | // call the desired Endpoint 46 | HttpResponseMessage response = null; 47 | // standard search 48 | response = await httpClient.GetAsync($"/api/product/search/{searchTerm}"); 49 | 50 | var responseText = await response.Content.ReadAsStringAsync(); 51 | 52 | _logger.LogInformation($"Http status code: {response.StatusCode}"); 53 | _logger.LogInformation($"Http response content: {responseText}"); 54 | 55 | if (response.IsSuccessStatusCode) 56 | return await response.Content.ReadFromJsonAsync(); 57 | 58 | } catch (Exception ex) { 59 | _logger.LogError(ex, "Error during Search."); 60 | } 61 | 62 | return new SearchResponse { Response = "No response" }; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/Store.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | enable 5 | enable 6 | preview 7 | 82f975cb-37d5-41d2-b6cc-a7add5c54c9b 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "DetailedErrors": true, 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft.AspNetCore": "Warning" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "ProductEndpoint": "http://localhost:5228/", 11 | "ProductEndpointHttps": "" 12 | } 13 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/wwwroot/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | 5 | h1:focus { 6 | outline: none; 7 | } 8 | 9 | a, .btn-link { 10 | color: #0071c1; 11 | } 12 | 13 | .btn-primary { 14 | color: #fff; 15 | background-color: #1b6ec2; 16 | border-color: #1861ac; 17 | } 18 | 19 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 20 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 21 | } 22 | 23 | .content { 24 | padding-top: 1.1rem; 25 | } 26 | 27 | .valid.modified:not([type=checkbox]) { 28 | outline: 1px solid #26b050; 29 | } 30 | 31 | .invalid { 32 | outline: 1px solid red; 33 | } 34 | 35 | .validation-message { 36 | color: red; 37 | } 38 | 39 | .blazor-error-boundary { 40 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 41 | padding: 1rem 1rem 1rem 3.7rem; 42 | color: white; 43 | } 44 | 45 | .blazor-error-boundary::after { 46 | content: "An error has occurred." 47 | } 48 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/Store/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Dotnet/src/eShopLite/Store/wwwroot/favicon.png -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/VectorEntities/ProductVector.cs: -------------------------------------------------------------------------------- 1 | using DataEntities; 2 | using Microsoft.Extensions.VectorData; 3 | 4 | namespace VectorEntities 5 | { 6 | public class ProductVector : Product 7 | { 8 | [VectorStoreRecordKey] 9 | public override int Id { get => base.Id; set => base.Id = value; } 10 | 11 | [VectorStoreRecordData] 12 | public override string? Name { get => base.Name; set => base.Name = value; } 13 | 14 | [VectorStoreRecordData] 15 | public override string? Description { get => base.Description; set => base.Description = value; } 16 | 17 | [VectorStoreRecordData] 18 | public override decimal Price { get => base.Price; set => base.Price = value; } 19 | 20 | [VectorStoreRecordVector(384, DistanceFunction.CosineSimilarity)] 21 | public ReadOnlyMemory Vector { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/VectorEntities/ProductVectorAzureAISearch.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.VectorData; 2 | 3 | namespace VectorEntities 4 | { 5 | public class ProductVectorAzureAISearch 6 | { 7 | [VectorStoreRecordKey] 8 | public string Id { get; set; } 9 | 10 | [VectorStoreRecordData] 11 | public string? Name { get; set; } 12 | 13 | [VectorStoreRecordData] 14 | public string? Description { get; set; } 15 | 16 | [VectorStoreRecordData] 17 | public string Price { get; set; } 18 | 19 | [VectorStoreRecordData] 20 | public string ImageUrl { get; set; } 21 | 22 | [VectorStoreRecordVector(1536, DistanceFunction.CosineSimilarity)] 23 | public ReadOnlyMemory Vector { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/VectorEntities/VectorEntities.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/.gitignore: -------------------------------------------------------------------------------- 1 | .azure 2 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | 3 | var builder = DistributedApplication.CreateBuilder(args); 4 | 5 | 6 | var products = builder.AddProject("products"); 7 | 8 | var store = builder.AddProject("store") 9 | .WithReference(products) 10 | .WithExternalHttpEndpoints(); 11 | 12 | builder.Build().Run(); 13 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "https": { 5 | "commandName": "Project", 6 | "dotnetRunMessages": true, 7 | "launchBrowser": true, 8 | "applicationUrl": "https://localhost:17104;http://localhost:15295", 9 | "environmentVariables": { 10 | "ASPNETCORE_ENVIRONMENT": "Development", 11 | "DOTNET_ENVIRONMENT": "Development", 12 | "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21237", 13 | "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22003" 14 | } 15 | }, 16 | "http": { 17 | "commandName": "Project", 18 | "dotnetRunMessages": true, 19 | "launchBrowser": true, 20 | "applicationUrl": "http://localhost:15295", 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development", 23 | "DOTNET_ENVIRONMENT": "Development", 24 | "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19009", 25 | "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20136" 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning", 6 | "Aspire.Hosting.Dcp": "Warning" 7 | } 8 | }, 9 | "Azure": { 10 | "CredentialSource": "AzureCli" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/azure.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json 2 | 3 | name: e-shop-app-host 4 | services: 5 | app: 6 | language: dotnet 7 | project: ./eShopAppHost.csproj 8 | host: containerapp 9 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/eShopAppHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | true 9 | 3e9fd9fb-e2c0-4477-bbd3-ab03823266cd 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/infra/appInsights/appInsights.module.bicep: -------------------------------------------------------------------------------- 1 | @description('The location for the resource(s) to be deployed.') 2 | param location string = resourceGroup().location 3 | 4 | param applicationType string = 'web' 5 | 6 | param kind string = 'web' 7 | 8 | param logAnalyticsWorkspaceId string 9 | 10 | resource appInsights 'Microsoft.Insights/components@2020-02-02' = { 11 | name: take('appInsights-${uniqueString(resourceGroup().id)}', 260) 12 | kind: kind 13 | location: location 14 | properties: { 15 | Application_Type: applicationType 16 | WorkspaceResourceId: logAnalyticsWorkspaceId 17 | } 18 | tags: { 19 | 'aspire-resource-name': 'appInsights' 20 | } 21 | } 22 | 23 | output appInsightsConnectionString string = appInsights.properties.ConnectionString -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/infra/azureaisearch/azureaisearch.module.bicep: -------------------------------------------------------------------------------- 1 | @description('The location for the resource(s) to be deployed.') 2 | param location string = resourceGroup().location 3 | 4 | param principalId string 5 | 6 | param principalType string 7 | 8 | resource azureaisearch 'Microsoft.Search/searchServices@2023-11-01' = { 9 | name: take('azureaisearch-${uniqueString(resourceGroup().id)}', 60) 10 | location: location 11 | properties: { 12 | hostingMode: 'default' 13 | disableLocalAuth: true 14 | partitionCount: 1 15 | replicaCount: 1 16 | } 17 | sku: { 18 | name: 'basic' 19 | } 20 | tags: { 21 | 'aspire-resource-name': 'azureaisearch' 22 | } 23 | } 24 | 25 | resource azureaisearch_SearchIndexDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { 26 | name: guid(azureaisearch.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7')) 27 | properties: { 28 | principalId: principalId 29 | roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7') 30 | principalType: principalType 31 | } 32 | scope: azureaisearch 33 | } 34 | 35 | resource azureaisearch_SearchServiceContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { 36 | name: guid(azureaisearch.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')) 37 | properties: { 38 | principalId: principalId 39 | roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0') 40 | principalType: principalType 41 | } 42 | scope: azureaisearch 43 | } 44 | 45 | output connectionString string = 'Endpoint=https://${azureaisearch.name}.search.windows.net' -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/infra/cache.tmpl.yaml: -------------------------------------------------------------------------------- 1 | api-version: 2024-02-02-preview 2 | location: {{ .Env.AZURE_LOCATION }} 3 | identity: 4 | type: UserAssigned 5 | userAssignedIdentities: 6 | ? "{{ .Env.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID }}" 7 | : {} 8 | properties: 9 | environmentId: {{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_ID }} 10 | configuration: 11 | activeRevisionsMode: single 12 | runtime: 13 | dotnet: 14 | autoConfigureDataProtection: true 15 | ingress: 16 | external: false 17 | targetPort: 6379 18 | transport: tcp 19 | allowInsecure: false 20 | registries: 21 | - server: {{ .Env.AZURE_CONTAINER_REGISTRY_ENDPOINT }} 22 | identity: {{ .Env.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID }} 23 | template: 24 | containers: 25 | - image: {{ .Image }} 26 | name: cache 27 | env: 28 | - name: AZURE_CLIENT_ID 29 | value: {{ .Env.MANAGED_IDENTITY_CLIENT_ID }} 30 | scale: 31 | minReplicas: 1 32 | tags: 33 | azd-service-name: cache 34 | aspire-resource-name: cache 35 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/infra/cache/cache.module.bicep: -------------------------------------------------------------------------------- 1 | @description('The location for the resource(s) to be deployed.') 2 | param location string = resourceGroup().location 3 | 4 | param principalId string 5 | 6 | param principalName string 7 | 8 | resource cache 'Microsoft.Cache/redis@2024-03-01' = { 9 | name: take('cache-${uniqueString(resourceGroup().id)}', 63) 10 | location: location 11 | properties: { 12 | sku: { 13 | name: 'Basic' 14 | family: 'C' 15 | capacity: 1 16 | } 17 | enableNonSslPort: false 18 | disableAccessKeyAuthentication: true 19 | minimumTlsVersion: '1.2' 20 | redisConfiguration: { 21 | 'aad-enabled': 'true' 22 | } 23 | } 24 | tags: { 25 | 'aspire-resource-name': 'cache' 26 | } 27 | } 28 | 29 | resource cache_contributor 'Microsoft.Cache/redis/accessPolicyAssignments@2024-03-01' = { 30 | name: take('cachecontributor${uniqueString(resourceGroup().id)}', 24) 31 | properties: { 32 | accessPolicyName: 'Data Contributor' 33 | objectId: principalId 34 | objectIdAlias: principalName 35 | } 36 | parent: cache 37 | } 38 | 39 | output connectionString string = '${cache.properties.hostName},ssl=true' -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/infra/main.bicep: -------------------------------------------------------------------------------- 1 | targetScope = 'subscription' 2 | 3 | @minLength(1) 4 | @maxLength(64) 5 | @description('Name of the environment that can be used as part of naming resource convention, the name of the resource group for your application will use this name, prefixed with rg-') 6 | param environmentName string 7 | 8 | @minLength(1) 9 | @description('The location used for all deployed resources') 10 | param location string 11 | 12 | @description('Id of the user or app to assign application roles') 13 | param principalId string = '' 14 | 15 | @metadata({azd: { 16 | type: 'generate' 17 | config: {length:22,minLower:1,minUpper:1,minNumeric:1} 18 | } 19 | }) 20 | @secure() 21 | param sql_password string 22 | 23 | var tags = { 24 | 'azd-env-name': environmentName 25 | } 26 | 27 | resource rg 'Microsoft.Resources/resourceGroups@2022-09-01' = { 28 | name: 'rg-${environmentName}' 29 | location: location 30 | tags: tags 31 | } 32 | 33 | module resources 'resources.bicep' = { 34 | scope: rg 35 | name: 'resources' 36 | params: { 37 | location: location 38 | tags: tags 39 | principalId: principalId 40 | } 41 | } 42 | 43 | module appInsights 'appInsights/appInsights.module.bicep' = { 44 | name: 'appInsights' 45 | scope: rg 46 | params: { 47 | location: location 48 | logAnalyticsWorkspaceId: resources.outputs.AZURE_LOG_ANALYTICS_WORKSPACE_ID 49 | } 50 | } 51 | module azureaisearch 'azureaisearch/azureaisearch.module.bicep' = { 52 | name: 'azureaisearch' 53 | scope: rg 54 | params: { 55 | location: location 56 | principalId: resources.outputs.MANAGED_IDENTITY_PRINCIPAL_ID 57 | principalType: 'ServicePrincipal' 58 | } 59 | } 60 | module openai 'openai/openai.module.bicep' = { 61 | name: 'openai' 62 | scope: rg 63 | params: { 64 | location: location 65 | principalId: resources.outputs.MANAGED_IDENTITY_PRINCIPAL_ID 66 | principalType: 'ServicePrincipal' 67 | } 68 | } 69 | output MANAGED_IDENTITY_CLIENT_ID string = resources.outputs.MANAGED_IDENTITY_CLIENT_ID 70 | output MANAGED_IDENTITY_NAME string = resources.outputs.MANAGED_IDENTITY_NAME 71 | output AZURE_LOG_ANALYTICS_WORKSPACE_NAME string = resources.outputs.AZURE_LOG_ANALYTICS_WORKSPACE_NAME 72 | output AZURE_CONTAINER_REGISTRY_ENDPOINT string = resources.outputs.AZURE_CONTAINER_REGISTRY_ENDPOINT 73 | output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = resources.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID 74 | output AZURE_CONTAINER_REGISTRY_NAME string = resources.outputs.AZURE_CONTAINER_REGISTRY_NAME 75 | output AZURE_CONTAINER_APPS_ENVIRONMENT_NAME string = resources.outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_NAME 76 | output AZURE_CONTAINER_APPS_ENVIRONMENT_ID string = resources.outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_ID 77 | output AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN string = resources.outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN 78 | output SERVICE_SQL_VOLUME_ESHOPAPPHOSTC8479139E4SQLDATA_NAME string = resources.outputs.SERVICE_SQL_VOLUME_ESHOPAPPHOSTC8479139E4SQLDATA_NAME 79 | output APPINSIGHTS_APPINSIGHTSCONNECTIONSTRING string = appInsights.outputs.appInsightsConnectionString 80 | output APPLICATIONINSIGHTS_CONNECTION_STRING string = appInsights.outputs.appInsightsConnectionString 81 | output AZUREAISEARCH_CONNECTIONSTRING string = azureaisearch.outputs.connectionString 82 | output OPENAI_CONNECTIONSTRING string = openai.outputs.connectionString 83 | output AZURE_VOLUMES_STORAGE_ACCOUNT string = resources.outputs.AZURE_VOLUMES_STORAGE_ACCOUNT 84 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/infra/main.parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "principalId": { 6 | "value": "${AZURE_PRINCIPAL_ID}" 7 | }, 8 | "sql_password": { 9 | "value": "${AZURE_SQL_PASSWORD}" 10 | }, 11 | "environmentName": { 12 | "value": "${AZURE_ENV_NAME}" 13 | }, 14 | "location": { 15 | "value": "${AZURE_LOCATION}" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/infra/openai/openai.module.bicep: -------------------------------------------------------------------------------- 1 | @description('The location for the resource(s) to be deployed.') 2 | param location string = resourceGroup().location 3 | 4 | param principalId string 5 | 6 | param principalType string 7 | 8 | resource openai 'Microsoft.CognitiveServices/accounts@2024-10-01' = { 9 | name: take('openai-${uniqueString(resourceGroup().id)}', 64) 10 | location: location 11 | kind: 'OpenAI' 12 | properties: { 13 | customSubDomainName: toLower(take(concat('openai', uniqueString(resourceGroup().id)), 24)) 14 | publicNetworkAccess: 'Enabled' 15 | disableLocalAuth: true 16 | } 17 | sku: { 18 | name: 'S0' 19 | } 20 | tags: { 21 | 'aspire-resource-name': 'openai' 22 | } 23 | } 24 | 25 | resource openai_CognitiveServicesOpenAIContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { 26 | name: guid(openai.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')) 27 | properties: { 28 | principalId: principalId 29 | roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442') 30 | principalType: principalType 31 | } 32 | scope: openai 33 | } 34 | 35 | resource gpt_4o_mini 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01' = { 36 | name: 'gpt-4o-mini' 37 | properties: { 38 | model: { 39 | format: 'OpenAI' 40 | name: 'gpt-4o-mini' 41 | version: '2024-07-18' 42 | } 43 | } 44 | sku: { 45 | name: 'GlobalStandard' 46 | capacity: 10 47 | } 48 | parent: openai 49 | } 50 | 51 | resource text_embedding_ada_002 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01' = { 52 | name: 'text-embedding-ada-002' 53 | properties: { 54 | model: { 55 | format: 'OpenAI' 56 | name: 'text-embedding-ada-002' 57 | version: '2' 58 | } 59 | } 60 | sku: { 61 | name: 'Standard' 62 | capacity: 8 63 | } 64 | parent: openai 65 | dependsOn: [ 66 | gpt_4o_mini 67 | ] 68 | } 69 | 70 | output connectionString string = 'Endpoint=${openai.properties.endpoint}' -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/infra/products.tmpl.yaml: -------------------------------------------------------------------------------- 1 | api-version: 2024-02-02-preview 2 | location: {{ .Env.AZURE_LOCATION }} 3 | identity: 4 | type: UserAssigned 5 | userAssignedIdentities: 6 | ? "{{ .Env.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID }}" 7 | : {} 8 | properties: 9 | environmentId: {{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_ID }} 10 | configuration: 11 | activeRevisionsMode: single 12 | runtime: 13 | dotnet: 14 | autoConfigureDataProtection: true 15 | ingress: 16 | external: false 17 | targetPort: {{ targetPortOrDefault 8080 }} 18 | transport: http 19 | allowInsecure: true 20 | registries: 21 | - server: {{ .Env.AZURE_CONTAINER_REGISTRY_ENDPOINT }} 22 | identity: {{ .Env.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID }} 23 | secrets: 24 | - name: applicationinsights-connection-string 25 | value: '{{ .Env.APPINSIGHTS_APPINSIGHTSCONNECTIONSTRING }}' 26 | - name: connectionstrings--azureaisearch 27 | value: '{{ .Env.AZUREAISEARCH_CONNECTIONSTRING }}' 28 | - name: connectionstrings--openai 29 | value: '{{ .Env.OPENAI_CONNECTIONSTRING }}' 30 | - name: connectionstrings--sqldb 31 | value: Server=sql,1433;User ID=sa;Password={{ securedParameter "sql_password" }};TrustServerCertificate=true;Database=sqldb 32 | template: 33 | containers: 34 | - image: {{ .Image }} 35 | name: products 36 | env: 37 | - name: AZURE_CLIENT_ID 38 | value: {{ .Env.MANAGED_IDENTITY_CLIENT_ID }} 39 | - name: AI_ChatDeploymentName 40 | value: gpt-4o-mini 41 | - name: AI_embeddingsDeploymentName 42 | value: text-embedding-ada-002 43 | - name: ASPNETCORE_FORWARDEDHEADERS_ENABLED 44 | value: "true" 45 | - name: HTTP_PORTS 46 | value: '{{ targetPortOrDefault 0 }}' 47 | - name: OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES 48 | value: "true" 49 | - name: OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES 50 | value: "true" 51 | - name: OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY 52 | value: in_memory 53 | - name: APPLICATIONINSIGHTS_CONNECTION_STRING 54 | secretRef: applicationinsights-connection-string 55 | - name: ConnectionStrings__azureaisearch 56 | secretRef: connectionstrings--azureaisearch 57 | - name: ConnectionStrings__openai 58 | secretRef: connectionstrings--openai 59 | - name: ConnectionStrings__sqldb 60 | secretRef: connectionstrings--sqldb 61 | scale: 62 | minReplicas: 1 63 | tags: 64 | azd-service-name: products 65 | aspire-resource-name: products 66 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/infra/sql.tmpl.yaml: -------------------------------------------------------------------------------- 1 | api-version: 2024-02-02-preview 2 | location: {{ .Env.AZURE_LOCATION }} 3 | identity: 4 | type: UserAssigned 5 | userAssignedIdentities: 6 | ? "{{ .Env.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID }}" 7 | : {} 8 | properties: 9 | environmentId: {{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_ID }} 10 | configuration: 11 | activeRevisionsMode: single 12 | runtime: 13 | dotnet: 14 | autoConfigureDataProtection: true 15 | ingress: 16 | external: false 17 | targetPort: 1433 18 | transport: tcp 19 | allowInsecure: false 20 | registries: 21 | - server: {{ .Env.AZURE_CONTAINER_REGISTRY_ENDPOINT }} 22 | identity: {{ .Env.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID }} 23 | secrets: 24 | - name: mssql-sa-password 25 | value: '{{ securedParameter "sql_password" }}' 26 | template: 27 | volumes: 28 | - name: sql-eshopapphostc8479139e4sqldata 29 | storageType: AzureFile 30 | storageName: {{ .Env.SERVICE_SQL_VOLUME_ESHOPAPPHOSTC8479139E4SQLDATA_NAME }} 31 | containers: 32 | - image: {{ .Image }} 33 | name: sql 34 | env: 35 | - name: AZURE_CLIENT_ID 36 | value: {{ .Env.MANAGED_IDENTITY_CLIENT_ID }} 37 | - name: ACCEPT_EULA 38 | value: "Y" 39 | - name: MSSQL_SA_PASSWORD 40 | secretRef: mssql-sa-password 41 | volumeMounts: 42 | - volumeName: sql-eshopapphostc8479139e4sqldata 43 | mountPath: /var/opt/mssql 44 | scale: 45 | minReplicas: 1 46 | tags: 47 | azd-service-name: sql 48 | aspire-resource-name: sql 49 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopAppHost/infra/store.tmpl.yaml: -------------------------------------------------------------------------------- 1 | api-version: 2024-02-02-preview 2 | location: {{ .Env.AZURE_LOCATION }} 3 | identity: 4 | type: UserAssigned 5 | userAssignedIdentities: 6 | ? "{{ .Env.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID }}" 7 | : {} 8 | properties: 9 | environmentId: {{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_ID }} 10 | configuration: 11 | activeRevisionsMode: single 12 | runtime: 13 | dotnet: 14 | autoConfigureDataProtection: true 15 | ingress: 16 | external: true 17 | targetPort: {{ targetPortOrDefault 8080 }} 18 | transport: http 19 | allowInsecure: false 20 | registries: 21 | - server: {{ .Env.AZURE_CONTAINER_REGISTRY_ENDPOINT }} 22 | identity: {{ .Env.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID }} 23 | secrets: 24 | - name: applicationinsights-connection-string 25 | value: '{{ .Env.APPINSIGHTS_APPINSIGHTSCONNECTIONSTRING }}' 26 | - name: connectionstrings--azureaisearch 27 | value: '{{ .Env.AZUREAISEARCH_CONNECTIONSTRING }}' 28 | template: 29 | containers: 30 | - image: {{ .Image }} 31 | name: store 32 | env: 33 | - name: AZURE_CLIENT_ID 34 | value: {{ .Env.MANAGED_IDENTITY_CLIENT_ID }} 35 | - name: ASPNETCORE_FORWARDEDHEADERS_ENABLED 36 | value: "true" 37 | - name: HTTP_PORTS 38 | value: '{{ targetPortOrDefault 0 }}' 39 | - name: OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES 40 | value: "true" 41 | - name: OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES 42 | value: "true" 43 | - name: OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY 44 | value: in_memory 45 | - name: services__products__http__0 46 | value: http://products.internal.{{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN }} 47 | - name: services__products__https__0 48 | value: https://products.internal.{{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN }} 49 | - name: APPLICATIONINSIGHTS_CONNECTION_STRING 50 | secretRef: applicationinsights-connection-string 51 | - name: ConnectionStrings__azureaisearch 52 | secretRef: connectionstrings--azureaisearch 53 | scale: 54 | minReplicas: 1 55 | tags: 56 | azd-service-name: store 57 | aspire-resource-name: store 58 | -------------------------------------------------------------------------------- /Dotnet/src/eShopLite/eShopServiceDefaults/eShopServiceDefaults.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0 4 | enable 5 | enable 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Java/.gitattributes: -------------------------------------------------------------------------------- 1 | /mvnw text eol=lf 2 | *.cmd text eol=crlf 3 | -------------------------------------------------------------------------------- /Java/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | *.properties 35 | -------------------------------------------------------------------------------- /Java/challenges/Challenge-05.md: -------------------------------------------------------------------------------- 1 | ### [< Previous Challenge](./Challenge-04.md) - **[Home](../README.md)** - 2 | 3 | # Challenge 05 - Prompt as Plugin 4 | 5 | 6 | This project demonstrates the integration of a Java Semantic Kernel with the PROMPT plugin. The Semantic Kernel is designed to facilitate natural language processing tasks, while the PROMPT plugin enhances its capabilities by providing advanced prompt engineering features. 7 | 8 | #### :Features 9 | 10 | * Natural Language Processing: Leverage the power of the Semantic Kernel for various NLP tasks. 11 | * Prompt Engineering: Utilize the PROMPT plugin to create, manage, and optimize prompts for different use cases. 12 | * Extensibility: Easily extend the kernel with additional plugins and custom functionalities. 13 | 14 | ## Description 15 | 16 | * Use the URL (http://localhost:3000) and submit the prompt 17 | 18 | ```txt 19 | I need some recipes made up Avocado and Black Bean 20 | ``` 21 | 22 | ## File in used 23 | * Prompt file name : Springboot-SK-Sample\src\main\resources\promptconfig\food\skprompt.txt 24 | * Configuration file name : Springboot-SK-Sample\src\main\resources\promptconfig\food\config.json 25 | 26 | ## implementation class 27 | Springboot-SK-Sample\src\main\java\com\sk\chapters\chapter5.java 28 | 29 | ### [< Previous Challenge](./Challenge-04.md) - **[Home](../README.md)** 30 | -------------------------------------------------------------------------------- /Java/challenges/Resources/Lectures.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/Lectures.pdf -------------------------------------------------------------------------------- /Java/challenges/Resources/Supporting Challenges/Challenge-00-DevBox.md: -------------------------------------------------------------------------------- 1 | ### [< Back to Challenge 0](../../Challenge-00.md) 2 | 3 | ## Why use Azure DevBox for this workshop? 4 | 5 | If you are using a tenant/subscription provided by Microsoft for this workshop then we have created an Azure DevBox just incase you are running into issues on your own computer or your IT department block developer certs or localhost. 6 | 7 | ## What is Azure DevBox? 8 | 9 | Azure DevBox is a cloud-based workstation that gives you a complete developer environment in the cloud. It’s already configured, so you can start coding quickly. However it will take about 25 minutes to deploy. 10 | 11 | ## How to Create Your Dev Box 12 | 13 | 1. Go to [Azure DevBox](https://devbox.microsoft.com/). 14 | 1. Sign in using the credentials given when you registered. 15 | 1. Click to create your Dev Box, no additional configuration is needed. 16 | 17 | ## Logging In 18 | 19 | 1. Once your Dev Box is ready, sign in with your credentials. 20 | 1. You’ll see a Windows environment ready for coding. 21 | 22 | ## Clone the Repository and launch VS Code 23 | 24 | 1. Open a terminal (PowerShell or Command Prompt). 25 | 1. Run: 26 | 27 | ```bash 28 | git clone https://github.com/microsoft/ai-developer.git 29 | ``` 30 | 31 | 1. Change into the cloned directory: 32 | 33 | ```bash 34 | cd ai-developer 35 | ``` 36 | 37 | 1. In the same terminal, enter: 38 | 39 | ```bash 40 | code . 41 | ``` 42 | 43 | 1. When prompted in VS Code to install recommended extensions, choose Yes to get everything you need for development. 44 | 45 | ![install recommended extensions](../images/recommendextensions.png) 46 | 47 | > [!NOTE] 48 | > If you don't see the popup, then just install the recommended extensions. 49 | -------------------------------------------------------------------------------- /Java/challenges/Resources/images/CORS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/CORS.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/admin-center-settings-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/admin-center-settings-icon.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/aoai-studio-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/aoai-studio-button.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/app_full_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/app_full_view.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/aspirehttps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/aspirehttps.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/ch0203.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/ch0203.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/ch02I02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/ch02I02.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/ch02i01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/ch02i01.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/ch0506.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/ch0506.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/ch05i01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/ch05i01.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/ch05i02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/ch05i02.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/ch05i03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/ch05i03.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/ch05i04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/ch05i04.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/ch05i05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/ch05i05.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/dotnet_version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/dotnet_version.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/filesforchallenges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/filesforchallenges.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/filter-blocklist-term.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/filter-blocklist-term.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/filter-blocklists.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/filter-blocklists.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/filter-deployment-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/filter-deployment-list.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/filter-name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/filter-name.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/filter-sliders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/filter-sliders.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/playvisualstudio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/playvisualstudio.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/recommendextensions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/recommendextensions.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/run_aspire_console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/run_aspire_console.png -------------------------------------------------------------------------------- /Java/challenges/Resources/images/text-embedding-ada-002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Java/challenges/Resources/images/text-embedding-ada-002.png -------------------------------------------------------------------------------- /Java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.4.3 9 | 10 | 11 | com.sk 12 | springboot_sk_rest_ai_tutorial 13 | 0.0.1-SNAPSHOT 14 | springboot_sk_rest_ai_tutorial 15 | Demo project for Spring Boot 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 17 31 | 1.3.0 32 | 3.1.2 33 | 3.11.0 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-web 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-devtools 44 | runtime 45 | true 46 | 47 | 48 | com.azure 49 | azure-json 50 | 1.3.0 51 | 52 | 53 | 54 | com.microsoft.semantic-kernel 55 | semantickernel-api 56 | ${version.semantic-kernel} 57 | 58 | 59 | com.microsoft.semantic-kernel 60 | semantickernel-aiservices-openai 61 | ${version.semantic-kernel} 62 | 63 | 64 | com.microsoft.semantic-kernel 65 | semantickernel-experimental 66 | ${version.semantic-kernel} 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-maven-plugin 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Java/src/main/java/com/sk/SpringbootSkChatApplication.java: -------------------------------------------------------------------------------- 1 | package com.sk; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.ComponentScan; 6 | 7 | @SpringBootApplication 8 | @ComponentScan(basePackages = "com.sk") 9 | public class SpringbootSkChatApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(SpringbootSkChatApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Java/src/main/java/com/sk/config/AzureAIConfig.java: -------------------------------------------------------------------------------- 1 | package com.sk.config; 2 | 3 | 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | 8 | @Configuration 9 | public class AzureAIConfig { 10 | 11 | @Value("${spring.application.name}") 12 | private String applicationName; 13 | 14 | @Value("${spring.ai.azure.openai.api-key}") 15 | private String openAiApiKey; 16 | 17 | @Value("${spring.ai.azure.openai.endpoint}") 18 | private String openAiEndpoint; 19 | 20 | @Value("${spring.ai.azure.openai.chat.deployment-name}") 21 | private String chatDeploymentName; 22 | 23 | @Value("${client.geo.url}") 24 | private String geourl; 25 | 26 | 27 | @Value("${client.geo.key}") 28 | private String geokey; 29 | 30 | @Value("${client.weather.url}") 31 | private String weatherurl; 32 | 33 | 34 | @Value("${client.azure.search.endpoint}") 35 | private String azure_search_Endpoint; 36 | 37 | @Value("${client.azure.search.adminkey}") 38 | private String azure_search_key; 39 | 40 | @Value("${client.azure.search.index}") 41 | private String azure_search_indexname; 42 | 43 | @Value("${client.azure.search.embedding.deployment}") 44 | private String embeddingDeploymentName; 45 | 46 | public String getEmbeddingDeploymentName() { 47 | return embeddingDeploymentName; 48 | } 49 | 50 | public void setEmbeddingDeploymentName(String embeddingDeploymentName) { 51 | this.embeddingDeploymentName = embeddingDeploymentName; 52 | } 53 | 54 | public String getAzure_search_Endpoint() { 55 | return azure_search_Endpoint; 56 | } 57 | 58 | public void setAzure_search_Endpoint(String azure_search_Endpoint) { 59 | this.azure_search_Endpoint = azure_search_Endpoint; 60 | } 61 | 62 | public String getAzure_search_key() { 63 | return azure_search_key; 64 | } 65 | 66 | public void setAzure_search_key(String azure_search_key) { 67 | this.azure_search_key = azure_search_key; 68 | } 69 | 70 | public String getAzure_search_indexname() { 71 | return azure_search_indexname; 72 | } 73 | 74 | public void setAzure_search_indexname(String azure_search_indexname) { 75 | this.azure_search_indexname = azure_search_indexname; 76 | } 77 | 78 | public String getWeatherurl() { 79 | return weatherurl; 80 | } 81 | 82 | public void setWeatherurl(String weatherurl) { 83 | this.weatherurl = weatherurl; 84 | } 85 | 86 | public String getGeourl() { 87 | return geourl; 88 | } 89 | 90 | public void setGeourl(String geourl) { 91 | this.geourl = geourl; 92 | } 93 | 94 | public String getGeokey() { 95 | return geokey; 96 | } 97 | 98 | public void setGeokey(String geokey) { 99 | this.geokey = geokey; 100 | } 101 | 102 | public String getApplicationName() { 103 | return applicationName; 104 | } 105 | 106 | public void setApplicationName(String applicationName) { 107 | this.applicationName = applicationName; 108 | } 109 | 110 | public String getOpenAiApiKey() { 111 | return openAiApiKey; 112 | } 113 | 114 | public void setOpenAiApiKey(String openAiApiKey) { 115 | this.openAiApiKey = openAiApiKey; 116 | } 117 | 118 | public String getOpenAiEndpoint() { 119 | return openAiEndpoint; 120 | } 121 | 122 | public void setOpenAiEndpoint(String openAiEndpoint) { 123 | this.openAiEndpoint = openAiEndpoint; 124 | } 125 | 126 | public String getChatDeploymentName() { 127 | return chatDeploymentName; 128 | } 129 | 130 | public void setChatDeploymentName(String chatDeploymentName) { 131 | this.chatDeploymentName = chatDeploymentName; 132 | } 133 | 134 | } 135 | 136 | -------------------------------------------------------------------------------- /Java/src/main/java/com/sk/config/CorsConfig.java: -------------------------------------------------------------------------------- 1 | package com.sk.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 7 | 8 | @Configuration 9 | public class CorsConfig { 10 | 11 | @Bean 12 | public WebMvcConfigurer corsConfigurer() { 13 | return new WebMvcConfigurer() { 14 | @Override 15 | public void addCorsMappings(CorsRegistry registry) { 16 | registry.addMapping("/api/**") 17 | .allowedOrigins("http://localhost:3000") 18 | //.allowedOrigins("*") // Allow all origins 19 | //.allowedHeaders("*") 20 | .allowedMethods("GET", "POST", "PUT", "DELETE") 21 | .allowCredentials(true); 22 | } 23 | }; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Java/src/main/java/com/sk/config/RestConfig.java: -------------------------------------------------------------------------------- 1 | package com.sk.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.client.RestTemplate; 6 | 7 | @Configuration 8 | public class RestConfig{ 9 | @Bean 10 | public RestTemplate restTemplate() { 11 | return new RestTemplate(); 12 | } 13 | } -------------------------------------------------------------------------------- /Java/src/main/java/com/sk/controller/AIController.java: -------------------------------------------------------------------------------- 1 | package com.sk.controller; 2 | 3 | import com.azure.ai.openai.OpenAIAsyncClient; 4 | import com.azure.ai.openai.OpenAIClientBuilder; 5 | import com.azure.core.credential.AzureKeyCredential; 6 | import com.microsoft.semantickernel.Kernel; 7 | import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatCompletion; 8 | import com.microsoft.semantickernel.orchestration.InvocationContext; 9 | import com.microsoft.semantickernel.orchestration.InvocationReturnMode; 10 | import com.microsoft.semantickernel.orchestration.ToolCallBehavior; 11 | import com.microsoft.semantickernel.plugin.KernelPlugin; 12 | import com.microsoft.semantickernel.plugin.KernelPluginFactory; 13 | import com.microsoft.semantickernel.services.ServiceNotFoundException; 14 | import com.microsoft.semantickernel.services.chatcompletion.ChatCompletionService; 15 | import com.microsoft.semantickernel.services.chatcompletion.ChatHistory; 16 | import com.microsoft.semantickernel.services.chatcompletion.ChatMessageContent; 17 | import com.sk.config.AzureAIConfig; 18 | import com.sk.kernel.kernelUtil; 19 | import com.sk.model.ChatRequest; 20 | import com.sk.plugins.AISearchPlugin; 21 | import com.sk.plugins.DateTimePlugin; 22 | import com.sk.plugins.GeocodingPlugin; 23 | import com.sk.plugins.WeatherPlugin; 24 | import com.sk.service.AIService; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.http.ResponseEntity; 27 | import org.springframework.web.bind.annotation.*; 28 | import org.springframework.web.client.RestTemplate; 29 | 30 | import java.io.IOException; 31 | import java.nio.file.Path; 32 | import java.util.List; 33 | 34 | @RestController 35 | @RequestMapping("/api") 36 | public class AIController { 37 | 38 | @Autowired 39 | AIService aiService; 40 | 41 | @GetMapping("/hello") 42 | public String getAI() { 43 | return "AI"; 44 | } 45 | 46 | 47 | 48 | @PostMapping("/skChat") 49 | public ResponseEntity>> getskChat(@RequestBody ChatRequest chatRequest) throws IOException, ServiceNotFoundException { 50 | return ResponseEntity.ok(aiService.getAIResponse(chatRequest)); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /Java/src/main/java/com/sk/kernel/kernelUtil.java: -------------------------------------------------------------------------------- 1 | package com.sk.kernel; 2 | 3 | 4 | import com.azure.ai.openai.OpenAIAsyncClient; 5 | import com.azure.ai.openai.OpenAIClientBuilder; 6 | import com.azure.core.credential.AzureKeyCredential; 7 | import com.azure.core.credential.KeyCredential; 8 | import com.microsoft.semantickernel.Kernel; 9 | import com.microsoft.semantickernel.aiservices.openai.chatcompletion.OpenAIChatCompletion; 10 | import com.microsoft.semantickernel.plugin.KernelPlugin; 11 | import com.microsoft.semantickernel.services.chatcompletion.ChatCompletionService; 12 | 13 | import com.sk.config.AzureAIConfig; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.stereotype.Component; 16 | 17 | import java.io.IOException; 18 | import java.util.List; 19 | 20 | @Component 21 | public class kernelUtil { 22 | 23 | @Autowired 24 | AzureAIConfig config; 25 | 26 | public Kernel kernelBuilder(List plugins) throws IOException { 27 | 28 | OpenAIAsyncClient clientasync = new OpenAIClientBuilder() 29 | .credential(new AzureKeyCredential(config.getOpenAiApiKey())) 30 | .endpoint(config.getOpenAiEndpoint()) 31 | .buildAsyncClient(); 32 | 33 | 34 | ChatCompletionService chatCompletion = OpenAIChatCompletion.builder() 35 | .withOpenAIAsyncClient(clientasync) 36 | .withModelId(config.getChatDeploymentName()) 37 | .build(); 38 | 39 | Kernel kernel = null; 40 | 41 | // Initialize the kernel with the chat completion service and the plugins 42 | Kernel.Builder kernelBuilder = Kernel.builder() 43 | .withAIService(ChatCompletionService.class, chatCompletion); 44 | 45 | for (KernelPlugin plugin : plugins) { 46 | kernelBuilder.withPlugin(plugin); 47 | } 48 | return kernelBuilder.build(); 49 | 50 | 51 | } 52 | 53 | public Kernel kernelBuilderWithoutPlugin() throws IOException { 54 | 55 | // Challenge 2 for Create the client 56 | OpenAIAsyncClient clientasync = new OpenAIClientBuilder() 57 | .credential(new AzureKeyCredential(config.getOpenAiApiKey())) 58 | .endpoint(config.getOpenAiEndpoint()) 59 | .buildAsyncClient(); 60 | 61 | // Challenge 2 Create the chat completion service 62 | ChatCompletionService chatCompletion = OpenAIChatCompletion.builder() 63 | .withOpenAIAsyncClient(clientasync) 64 | .withModelId(config.getChatDeploymentName()) 65 | .build(); 66 | 67 | Kernel kernel = null; 68 | 69 | kernel = Kernel.builder() 70 | .withAIService(ChatCompletionService.class, chatCompletion) 71 | .build(); 72 | return kernel; 73 | 74 | } 75 | 76 | public OpenAIAsyncClient openAIAsyncClient() throws IOException { 77 | 78 | if (config.getOpenAiEndpoint() != null && !config.getOpenAiEndpoint().isEmpty()) { 79 | return new OpenAIClientBuilder() 80 | .endpoint(config.getOpenAiEndpoint()) 81 | .credential(new AzureKeyCredential(config.getOpenAiApiKey())) 82 | .buildAsyncClient(); 83 | } 84 | return new OpenAIClientBuilder() 85 | .credential(new KeyCredential(config.getOpenAiApiKey())) 86 | .buildAsyncClient(); 87 | } 88 | 89 | 90 | } 91 | -------------------------------------------------------------------------------- /Java/src/main/java/com/sk/model/ChatRequest.java: -------------------------------------------------------------------------------- 1 | package com.sk.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import java.util.List; 5 | 6 | public class ChatRequest { 7 | 8 | @JsonProperty("messages") 9 | private List messages; 10 | 11 | public List getMessages() { 12 | return messages; 13 | } 14 | 15 | public void setMessages(List messages) { 16 | this.messages = messages; 17 | } 18 | 19 | public static class Message { 20 | private String role; 21 | private String content; 22 | 23 | public String getRole() { 24 | return role; 25 | } 26 | 27 | public void setRole(String role) { 28 | this.role = role; 29 | } 30 | 31 | public String getContent() { 32 | return content; 33 | } 34 | 35 | public void setContent(String content) { 36 | this.content = content; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Java/src/main/java/com/sk/model/Handbook.java: -------------------------------------------------------------------------------- 1 | package com.sk.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.microsoft.semantickernel.data.vectorstorage.attributes.VectorStoreRecordDataAttribute; 5 | import com.microsoft.semantickernel.data.vectorstorage.attributes.VectorStoreRecordKeyAttribute; 6 | import com.microsoft.semantickernel.data.vectorstorage.attributes.VectorStoreRecordVectorAttribute; 7 | import com.microsoft.semantickernel.data.vectorstorage.definition.DistanceFunction; 8 | 9 | import java.nio.charset.StandardCharsets; 10 | import java.util.Base64; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | public class Handbook { 15 | @VectorStoreRecordKeyAttribute 16 | private String chunk_id; 17 | @VectorStoreRecordDataAttribute 18 | private String parent_id; 19 | @VectorStoreRecordDataAttribute 20 | private String chunk; 21 | @VectorStoreRecordDataAttribute 22 | private String title; 23 | 24 | public String getChunk_id() { 25 | return chunk_id; 26 | } 27 | 28 | public void setChunk_id(String chunk_id) { 29 | this.chunk_id = chunk_id; 30 | } 31 | 32 | public String getParent_id() { 33 | return parent_id; 34 | } 35 | 36 | public void setParent_id(String parent_id) { 37 | this.parent_id = parent_id; 38 | } 39 | 40 | public String getChunk() { 41 | return chunk; 42 | } 43 | 44 | public void setChunk(String chunk) { 45 | this.chunk = chunk; 46 | } 47 | 48 | public String getTitle() { 49 | return title; 50 | } 51 | 52 | public void setTitle(String title) { 53 | this.title = title; 54 | } 55 | 56 | public List getText_vector() { 57 | return text_vector; 58 | } 59 | 60 | public void setText_vector(List text_vector) { 61 | this.text_vector = text_vector; 62 | } 63 | 64 | /*@VectorStoreRecordDataAttribute 65 | private String chunk_id; 66 | @VectorStoreRecordDataAttribute 67 | private String last_updated;*/ 68 | @VectorStoreRecordVectorAttribute(dimensions = 1536, indexKind = "Hnsw", distanceFunction = DistanceFunction.COSINE_DISTANCE) 69 | private List text_vector; 70 | 71 | public Handbook() { 72 | this(null, null, null, null, null, /*null, null,*/ Collections.emptyList()); 73 | } 74 | 75 | public Handbook(@JsonProperty String chunk_id, @JsonProperty String parent_id, @JsonProperty String chunk, 76 | @JsonProperty String title, @JsonProperty String id,/* @JsonProperty String chunk_id, 77 | @JsonProperty String last_updated,*/ @JsonProperty List text_vector) { 78 | this.chunk_id = chunk_id; 79 | this.parent_id = parent_id; 80 | this.chunk = chunk; 81 | this.title = title; 82 | this.text_vector = text_vector; 83 | } 84 | 85 | static String encodeId(String realId) { 86 | byte[] bytes = Base64.getUrlEncoder().encode(realId.getBytes(StandardCharsets.UTF_8)); 87 | return new String(bytes, StandardCharsets.UTF_8); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Java/src/main/java/com/sk/plugins/DateTimePlugin.java: -------------------------------------------------------------------------------- 1 | package com.sk.plugins; 2 | 3 | import com.microsoft.semantickernel.semanticfunctions.annotations.DefineKernelFunction; 4 | import com.microsoft.semantickernel.semanticfunctions.annotations.KernelFunctionParameter; 5 | 6 | 7 | import java.time.LocalDate; 8 | 9 | public class DateTimePlugin { 10 | 11 | 12 | 13 | @DefineKernelFunction(description = "Get the current date", name = "getCurrentDate") 14 | public String getCurrentDate() { 15 | try { 16 | System.out.println("-----getCurrentDate----->"+LocalDate.now().toString()); 17 | return LocalDate.now().toString(); 18 | } catch (Exception e) { 19 | return "Error: " + e.getMessage(); 20 | } 21 | } 22 | 23 | @DefineKernelFunction(description = "Get the current time", name = "getCurrentTime") 24 | public String getCurrentTime() { 25 | try { 26 | System.out.println("-----getCurrentTime----->"+java.time.LocalTime.now().toString()); 27 | //Return the current time in local time zone 28 | return java.time.LocalTime.now().toString(); 29 | 30 | } catch (Exception e) { 31 | return "Error: " + e.getMessage(); 32 | } 33 | } 34 | 35 | @DefineKernelFunction(description = "Get the year from a given date", name = "getYear") 36 | public String getYear( 37 | @KernelFunctionParameter(name = "date", description = "The date in YYYY-MM-DD format") String date) { 38 | try { 39 | LocalDate localDate = LocalDate.parse(date); 40 | System.out.println("-----getYear-----> " + localDate.getYear()); 41 | return String.valueOf(localDate.getYear()); 42 | } catch (Exception e) { 43 | return "Error: Please provide date in YYYY-MM-DD format"; 44 | } 45 | } 46 | 47 | @DefineKernelFunction(description = "Get the month from a given date", name = "getMonth") 48 | public String getMonth( 49 | @KernelFunctionParameter(name = "date", description = "The date in YYYY-MM-DD format") String date) { 50 | try { 51 | LocalDate localDate = LocalDate.parse(date); 52 | String month = localDate.getMonth().toString(); 53 | System.out.println("-----getMonth-----> " + month); 54 | return month; 55 | } catch (Exception e) { 56 | return "Error: Please provide date in YYYY-MM-DD format"; 57 | } 58 | } 59 | 60 | @DefineKernelFunction(description = "Get the day of week from a given date", name = "getDayOfWeek") 61 | public String getDayOfWeek( 62 | @KernelFunctionParameter(name = "date", description = "The date in YYYY-MM-DD format") String date) { 63 | try { 64 | LocalDate localDate = LocalDate.parse(date); 65 | String dayOfWeek = localDate.getDayOfWeek().toString(); 66 | System.out.println("-----getDayOfWeek-----> " + dayOfWeek); 67 | return dayOfWeek; 68 | } catch (Exception e) { 69 | return "Error: Please provide date in YYYY-MM-DD format"; 70 | } 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /Java/src/main/java/com/sk/plugins/WeatherPlugin.java: -------------------------------------------------------------------------------- 1 | package com.sk.plugins; 2 | 3 | import com.microsoft.semantickernel.semanticfunctions.annotations.DefineKernelFunction; 4 | import com.microsoft.semantickernel.semanticfunctions.annotations.KernelFunctionParameter; 5 | import com.sk.config.AzureAIConfig; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.client.RestTemplate; 10 | 11 | import java.net.HttpURLConnection; 12 | import java.net.URL; 13 | import java.util.Scanner; 14 | 15 | @Component 16 | public class WeatherPlugin { 17 | 18 | private final AzureAIConfig config; 19 | 20 | private final RestTemplate restTemplate; 21 | 22 | @Autowired 23 | public WeatherPlugin(AzureAIConfig config, RestTemplate restTemplate) { 24 | this.config = config; 25 | this.restTemplate = restTemplate; 26 | } 27 | 28 | 29 | @DefineKernelFunction(description = "Gets the forecast for a given latitude, longitude and number of days. Can forecast up to 16 days in the future.", name = "getWeather") 30 | public String getWeather( 31 | @KernelFunctionParameter(name = "latitude", description = "The latitude coordinate") double latitude, 32 | @KernelFunctionParameter(name = "longitude", description = "The longitude coordinate") double longitude, 33 | @KernelFunctionParameter(name = "days", description = "Number of days") int days) { 34 | try { 35 | if (days <= 0 || days > 16) 36 | { 37 | return "Day count is out of bounds. Days should be between 1 and 16"; 38 | } 39 | String urlString = config.getWeatherurl()+"?latitude="+latitude+"&longitude="+longitude+"¤t=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,rain,showers,snowfall,weather_code,wind_speed_10m,wind_direction_10m,wind_gusts_10m&hourly=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation_probability,precipitation,rain,showers,snowfall,weather_code,cloud_cover,wind_speed_10m,uv_index&temperature_unit=fahrenheit&wind_speed_unit=mph&precipitation_unit=inch&forecast_days="+days; 40 | System.out.println("getWeather URL--> "+urlString); 41 | ResponseEntity response = restTemplate.getForEntity(urlString, String.class); 42 | return response.getBody(); 43 | } catch (Exception e) { 44 | return "Error: " + e.getMessage(); 45 | } 46 | } 47 | 48 | 49 | @DefineKernelFunction(description = "Gets the weather details for recent previous weather at a given location. This can go a number of days up to 3 months into the past.", name = "get_weather_recent") 50 | public String get_weather_recent(float latitude, float longitude, int daysInPast) 51 | { 52 | 53 | String urlString = config.getWeatherurl()+"?latitude="+latitude+"&longitude="+longitude+"&daily=weather_code,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min,sunrise,sunset,daylight_duration,uv_index_max,precipitation_sum,rain_sum,showers_sum,snowfall_sum,precipitation_hours,wind_speed_10m_max,wind_gusts_10m_max&temperature_unit=fahrenheit&wind_speed_unit=mph&precipitation_unit=inch&past_days="+daysInPast; 54 | System.out.println("get_weather_recent URL--> "+urlString); 55 | ResponseEntity response = restTemplate.getForEntity(urlString, String.class); 56 | return response.getBody(); 57 | } 58 | 59 | 60 | } -------------------------------------------------------------------------------- /Java/src/main/resources/application.properties.temp: -------------------------------------------------------------------------------- 1 | spring.application.name=SprintAI 2 | spring.ai.azure.openai.api-key= 3 | spring.ai.azure.openai.endpoint= 4 | spring.ai.azure.openai.chat.deployment-name=gpt-4 5 | 6 | #Configuration for GeoCode and location setting 7 | client.geo.url= 8 | client.geo.key= 9 | 10 | #Configuration for weather API 11 | client.weather.url= 12 | 13 | #Configuration for Azure Search 14 | client.azure.search.endpoint= 15 | client.azure.search.index= 16 | client.azure.search.adminkey= 17 | client.azure.search.embedding.deployment= 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Java/src/main/resources/promptconfig/food/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": 1, 3 | "type": "completion", 4 | "description": "Summarize given text or any text document", 5 | "completion": { 6 | "max_tokens": 1024, 7 | "temperature": 0.0, 8 | "top_p": 0.0, 9 | "presence_penalty": 0.0, 10 | "frequency_penalty": 0.0 11 | }, 12 | "input": { 13 | "parameters": [ 14 | { 15 | "name": "input", 16 | "description": "Text to summarize", 17 | "defaultValue": "" 18 | } 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Java/src/main/resources/promptconfig/food/skprompt.txt: -------------------------------------------------------------------------------- 1 | 2 | Instructions: You are a chef specializing in Mexican vegetarian cuisine. Based on the user's input, provide one detailed vegetarian recipe in just 100 words. 3 | 4 | Potato 5 | 6 | Vegetarian Dish: Potato and Black Bean Patties 7 | 8 | Ingredients: 9 | - 4 medium potatoes (boiled and mashed) 10 | - 1 cup black beans (cooked and mashed) 11 | - 1-2 jalapeños (finely chopped, optional) 12 | - 2 cloves garlic (minced) 13 | - 2 tbsp cilantro (finely chopped) 14 | - 2 tbsp cornmeal 15 | - 1/2 tsp chili powder 16 | - 1/2 tsp cumin powder 17 | - 1/2 tsp paprika 18 | - Salt to taste 19 | - Oil for frying 20 | 21 | Instructions: 22 | Mix potatoes, beans, jalapeños, garlic, cilantro, cornmeal, and spices. Shape into patties. Fry until golden. Serve with salsa or guacamole. 23 | 24 | {{$input}}](./Challenge-01.md) 2 | 3 | # Challenge 00 - Prerequisites - Ready, Set, GO! 4 | 5 | ## Introduction 6 | 7 | Thank you for participating in the OpenAI & Semantic Kernel Fundamentals. Before you can workshop, you will need to set up some prerequisites. 8 | 9 | ## Description 10 | 11 | In this challenge, you will set up the necessary prerequisites and environment to complete the rest of the workshop. 12 | 13 | ### Access Azure AI Foundry 14 | 15 | You will need an Azure subscription to complete this workshop. If you don't have one, choose one of the following options: 16 | 17 | - [Azure Subscription](https://azure.microsoft.com/en-us/free/) 18 | 19 | - Azure Passes: If your workshop group has access to an Azure Pass, you can utilize it to gain access to Azure AI Foundry and other necessary Azure resources for this workshop. Please see your instructor or event organizer for details on how to redeem your Azure Pass. 20 | 21 | ### Development Environment 22 | 23 | You will need a development environment to complete the challenges. You need to set up a local development environment on your workstation. 24 | 25 | #### Use Local Workstation 26 | 27 | Using your local workstation is another option to complete the challenges. You will need to set up the necessary tools and resources on your local workstation to complete the challenges and will need to clone this Git Repository to your local workstation. 28 | 29 | ##### Install the following tools on your Local Workstation: 30 | 31 | - IDE 32 | - [VS Code](https://code.visualstudio.com/download) 33 | - [VS code Python Extension](https://code.visualstudio.com/docs/languages/python) 34 | - Recommended: [GitHub Copilot Extension](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) 35 | - [Git CLI](https://git-scm.com/downloads) or [GitHub Desktop](https://github.com/apps/desktop) 36 | 37 | :bulb: **Note:** GitHub Copilot is a great tool to help you write code faster. You can install the [GitHub Copilot Extension](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) in Visual Studio Code. We highly recommend you install this extension to help you complete the challenges. 38 | 39 | If you do not have GitHub Copilot, you can still complete the challenges. However you can start a free trial of GitHub Copilot by following the instructions [here](https://github.com/features/copilot?ef_id=_k_fdbe5318644f1533620435c241c3e251_k_&OCID=AIDcmmb150vbv1_SEM__k_fdbe5318644f1533620435c241c3e251_k_&msclkid=fdbe5318644f1533620435c241c3e251). 40 | 41 | ##### Clone the resources to your Local Workstation 42 | 43 | From a directory where you want to store the resources, clone the [repository](https://github.com/microsoft/ai-developer). You can do this by running the following command in your terminal or command prompt: 44 | 45 | ```console 46 | git clone https://github.com/microsoft/ai-developer.git 47 | ``` 48 | 49 | The rest of the challenges will refer to the relative paths inside the Git Repository where you can find the various resources to complete the challenges. 50 | 51 | ## Success Criteria 52 | 53 | - Verify that you have **Visual Studio Code** with ***Python*** extension installed. 54 | - Verify you have the following files & folders locally: 55 | 56 | ```text 57 | ├─challenges 58 | ├─ python 59 | └──src 60 | └───plugins 61 | └───workitems 62 | └───chat.py 63 | └───app.py 64 | └───requirements.txt 65 | ``` 66 | 67 | ### **[Home](../README.md)** - [Next Challenge >](./Challenge-01.md) 68 | -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch01img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch01img1.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch02img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch02img1.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch02img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch02img2.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch02img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch02img3.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch05img0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch05img0.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch05img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch05img1.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch05img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch05img2.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch05img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch05img3.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch05img4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch05img4.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch05img4.png.old: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch05img4.png.old -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch05img5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch05img5.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch05img5.png.old: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch05img5.png.old -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch05img6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch05img6.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch05img6.png.old: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch05img6.png.old -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch05img7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch05img7.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch05img8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch05img8.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch05img8.png.old: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch05img8.png.old -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch06img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch06img1.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch06img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch06img2.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch06img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch06img3.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch06img4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch06img4.png -------------------------------------------------------------------------------- /Python/challenges/Resources/image/ch06img5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/challenges/Resources/image/ch06img5.png -------------------------------------------------------------------------------- /Python/src/.env_template: -------------------------------------------------------------------------------- 1 | GLOBAL_LLM_SERVICE="AzureOpenAI" 2 | AZURE_OPENAI_CHAT_DEPLOYMENT_NAME="" 3 | AZURE_OPENAI_ENDPOINT="" 4 | AZURE_OPENAI_API_KEY="" 5 | AZURE_OPENAI_API_VERSION="" 6 | AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME="" 7 | GEOCODING_API_KEY="" 8 | AZURE_AI_SEARCH_API_KEY="" 9 | AZURE_AI_SEARCH_ENDPOINT="" 10 | AZURE_AI_SEARCH_INDEX_NAME="" 11 | AZURE_OPENAI_TEXT_TO_IMAGE_DEPLOYMENT_NAME="" 12 | AZURE_TEXT_TO_IMAGE_ENDPOINT="" 13 | AZURE_TEXT_TO_IMAGE_API_KEY="" -------------------------------------------------------------------------------- /Python/src/chat.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | from dotenv import load_dotenv 4 | from semantic_kernel import Kernel 5 | from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion, AzureTextToImage, AzureChatPromptExecutionSettings 6 | from semantic_kernel.connectors.memory.azure_ai_search import AzureAISearchStore 7 | from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior 8 | from semantic_kernel.connectors.openapi_plugin import OpenAPIFunctionExecutionParameters 9 | from semantic_kernel.connectors.ai.open_ai import AzureTextEmbedding 10 | from semantic_kernel.contents.chat_history import ChatHistory 11 | from semantic_kernel.functions import KernelArguments 12 | import os 13 | from pathlib import Path 14 | 15 | from plugins.ai_search_plugin import AiSearchPlugin 16 | from plugins.geo_coding_plugin import GeoPlugin 17 | from plugins.image_plugin import ImagePlugin 18 | from plugins.time_plugin import TimePlugin 19 | from plugins.weather_plugin import WeatherPlugin 20 | 21 | # Add Logger 22 | logger = logging.getLogger(__name__) 23 | 24 | # Find and load the .env file from the src directory 25 | env_path = Path(__file__).parent / '.env' 26 | load_dotenv(dotenv_path=env_path, override=True) 27 | 28 | # Direct console output that will always be visible 29 | print("============ ENVIRONMENT VARIABLES ============") 30 | print(f"Using .env from: {env_path}") 31 | print(f"File exists: {env_path.exists()}") 32 | print(f"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: {os.environ.get('AZURE_OPENAI_CHAT_DEPLOYMENT_NAME')}") 33 | print(f"AZURE_OPENAI_ENDPOINT: {os.environ.get('AZURE_OPENAI_ENDPOINT')}") 34 | print(f"AZURE_OPENAI_API_KEY: {'*****' if os.environ.get('AZURE_OPENAI_API_KEY') else 'Not found'}") 35 | print(f"GEOCODING_API_KEY: {'*****' if os.environ.get('GEOCODING_API_KEY') else 'Not found'}") 36 | print(f"AZURE_OPENAI_API_VERSION: {os.environ.get('AZURE_OPENAI_API_VERSION')}") 37 | print(f"AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME: {os.environ.get('AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME')}") 38 | print(f"AZURE_AI_SEARCH_ENDPOINT: {os.environ.get('AZURE_AI_SEARCH_ENDPOINT')}") 39 | print(f"AZURE_AI_SEARCH_API_KEY: {'*****' if os.environ.get('AZURE_AI_SEARCH_API_KEY') else 'Not found'}") 40 | print(f"AZURE_AI_SEARCH_INDEX_NAME: {os.environ.get('AZURE_AI_SEARCH_INDEX_NAME')}") 41 | print(f"AZURE_OPENAI_TEXT_TO_IMAGE_DEPLOYMENT_NAME: {os.environ.get('AZURE_OPENAI_TEXT_TO_IMAGE_DEPLOYMENT_NAME')}") 42 | print("==============================================") 43 | 44 | chat_history = ChatHistory() 45 | 46 | def initialize_kernel(): 47 | #Challene 02 - Add Kernel 48 | 49 | #Challenge 02 - Chat Completion Service 50 | #Challenge 02- Add kernel to the chat completion service 51 | 52 | # Challenge 05 - Add Text Embedding Service 53 | 54 | 55 | # Challenge 07 - Add Text to Image Service 56 | 57 | 58 | return kernel 59 | 60 | 61 | async def process_message(user_input): 62 | kernel = initialize_kernel() 63 | 64 | # Challenge 03 - Add Time Plugin 65 | 66 | 67 | # Challenge 03 - Add Geo Plugin 68 | 69 | 70 | # CHallenge 03 - Add Weather Plugin 71 | 72 | 73 | # Challenge 04 - Import OpenAPI Spec 74 | 75 | 76 | # Challenge 05 - Add Search Plugin 77 | 78 | 79 | # Challenge 07 - Text To Image Plugin 80 | 81 | # Start Challenge 02 82 | 83 | # Get the chat completion service from the kernel 84 | 85 | # Add the user's message to chat history 86 | 87 | # Create settings for the chat request 88 | 89 | # Send the chat history to the AI and get a response 90 | 91 | # Add the AI's response to chat history 92 | 93 | return result 94 | 95 | def reset_chat_history(): 96 | global chat_history 97 | chat_history = ChatHistory() -------------------------------------------------------------------------------- /Python/src/data/employee_handbook.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/Python/src/data/employee_handbook.pdf -------------------------------------------------------------------------------- /Python/src/models/employee_handbook_model.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft. All rights reserved. 2 | 3 | 4 | from dataclasses import dataclass 5 | from typing import Annotated, Any 6 | 7 | from pydantic import BaseModel 8 | 9 | from semantic_kernel.connectors.ai.open_ai import OpenAIEmbeddingPromptExecutionSettings 10 | from semantic_kernel.data import ( 11 | VectorStoreRecordDataField, 12 | VectorStoreRecordKeyField, 13 | VectorStoreRecordVectorField, 14 | vectorstoremodel, 15 | ) 16 | 17 | ### 18 | # The data model used for this sample is based on the hotel data model from the Azure AI Search samples. 19 | # When deploying a new index in Azure AI Search using the import wizard you can choose to deploy the 'hotel-samples' 20 | # dataset, see here: https://learn.microsoft.com/en-us/azure/search/search-get-started-portal. 21 | # This is the dataset used in this sample with some modifications. 22 | # This model adds vectors for the 2 descriptions in English and French. 23 | # Both are based on the 1536 dimensions of the OpenAI models. 24 | # You can adjust this at creation time and then make the change below as well. 25 | ### 26 | 27 | 28 | @vectorstoremodel 29 | @dataclass 30 | class EmployeeHandbookModel(BaseModel): 31 | id: Annotated[str, VectorStoreRecordKeyField] 32 | content: Annotated[str, VectorStoreRecordDataField()] 33 | title: Annotated[str, VectorStoreRecordDataField()] 34 | url: Annotated[str, VectorStoreRecordDataField()] 35 | filepath: Annotated[str, VectorStoreRecordDataField()] 36 | meta_json_string: Annotated[str, VectorStoreRecordDataField()] 37 | contentVector: Annotated[ 38 | list[float] | None, 39 | VectorStoreRecordVectorField( 40 | dimensions=1536, 41 | local_embedding=True, 42 | embedding_settings={"embedding": OpenAIEmbeddingPromptExecutionSettings(dimensions=1536)}, 43 | ), 44 | ] = None 45 | 46 | -------------------------------------------------------------------------------- /Python/src/multi_agent.py: -------------------------------------------------------------------------------- 1 | import os 2 | import asyncio 3 | import logging 4 | from pathlib import Path 5 | from dotenv import load_dotenv 6 | from semantic_kernel import Kernel 7 | from semantic_kernel.agents import AgentGroupChat, ChatCompletionAgent 8 | from semantic_kernel.agents.strategies import ( 9 | KernelFunctionSelectionStrategy, 10 | KernelFunctionTerminationStrategy, 11 | ) 12 | from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion 13 | from semantic_kernel.contents import ChatHistoryTruncationReducer 14 | from semantic_kernel.functions import KernelFunctionFromPrompt 15 | 16 | # Add Logger 17 | logger = logging.getLogger(__name__) 18 | 19 | # Find and load the .env file from the src directory 20 | env_path = Path(__file__).parent / '.env' 21 | load_dotenv(dotenv_path=env_path, override=True) 22 | 23 | def create_kernel(): 24 | """Creates a Kernel instance with an Azure OpenAI ChatCompletion service.""" 25 | kernel = Kernel() 26 | kernel.add_service(service=AzureChatCompletion()) 27 | return kernel 28 | 29 | async def run_multi_agent(user_input: str): 30 | """ 31 | Run a multi-agent collaboration with Business Analyst, Software Engineer, and Product Owner. 32 | 33 | Args: 34 | user_input (str): The user's request for application development 35 | """ 36 | # Create a single kernel instance for all agents 37 | kernel = create_kernel() 38 | 39 | # Define agent names as constants 40 | # IMPORTANT: These names must match the ones used in the selection function 41 | # as they are used in the UI to help change how the agents are displayed 42 | BUSINESS_ANALYST_NAME = "BusinessAnalyst" 43 | SOFTWARE_ENGINEER_NAME = "SoftwareEngineer" 44 | PRODUCT_OWNER_NAME = "ProductOwner" 45 | 46 | # TODO: Step 1 - Create ChatCompletionAgents 47 | # Create three ChatCompletionAgent instances using the personas provided in the challenge 48 | # Each agent needs: kernel, name, and instructions 49 | # Use the BUSINESS_ANALYST_NAME, SOFTWARE_ENGINEER_NAME, and PRODUCT_OWNER_NAME constants 50 | 51 | 52 | # TODO: Step 2 - Define a selection function to determine which agent should take the next turn 53 | 54 | 55 | # TODO: Step 3 - Define a termination function 56 | # Create a KernelFunctionFromPrompt that checks if the Product Owner has approved the work 57 | # The termination keyword is "%APPR%" 58 | # The function should respond with the termination keyword when the Product Owner approves 59 | 60 | 61 | # TODO: Step 4 - Create history reducer to save tokens 62 | # Create a ChatHistoryTruncationReducer with target_count=3 to limit conversation history 63 | 64 | 65 | # TODO: Step 5 - Create the AgentGroupChat 66 | 67 | 68 | # TODO: Step 6 - Add the user's initial request to the chat 69 | 70 | 71 | responses = [] 72 | 73 | # TODO: Step 7 - Invoke the group chat and collect agent responses 74 | # Use an async for loop to iterate through group_chat.invoke() 75 | # Collect responses in the format: {"role": response.name, "message": response.content} 76 | 77 | logger.info("Multi-agent conversation complete.") 78 | return responses 79 | -------------------------------------------------------------------------------- /Python/src/plugins/geo_coding_plugin.py: -------------------------------------------------------------------------------- 1 | from typing import TypedDict, Annotated, Optional 2 | import requests 3 | import asyncio 4 | from semantic_kernel.functions import kernel_function 5 | import os 6 | from dotenv import load_dotenv 7 | 8 | load_dotenv(override=True) 9 | 10 | class GeoPlugin: 11 | 12 | @kernel_function(description="Gets the latitude and longitude for a location.") 13 | async def get_latitude_longitude(self, location:Annotated[str, "The name of the location"]): 14 | print(f"lat/long request location: {location}") 15 | url = f"https://geocode.maps.co/search?q={location}&api_key={os.getenv('GEOCODING_API_KEY')}" 16 | response = requests.get(url) 17 | data = response.json() 18 | position = data[0] 19 | return f"Latitude: {position['lat']}, Longitude: {position['lon']}" 20 | -------------------------------------------------------------------------------- /Python/src/requirements.txt: -------------------------------------------------------------------------------- 1 | semantic-kernel==1.31.0 2 | asyncio>=3.4.3 3 | python-dotenv>=1.0.0 4 | azure-search-documents>=11.4.0 5 | fastapi>=0.109.0 6 | pandas>=2.2.0 7 | uvicorn>=0.27.0 8 | streamlit>=1.31.0 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /misc/finalresult.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/ai-developer/bc0480d2e931ad20c0441aa9626698cfaa6004ca/misc/finalresult.zip --------------------------------------------------------------------------------