|
1 |
| -@rendermode InteractiveServer |
| 1 | +@* |
| 2 | +*********************************************************************** |
| 3 | +Author : glensouza |
| 4 | +Created : 01-14-2025 |
| 5 | +
|
| 6 | +Last Modified By : glensouza |
| 7 | +Last Modified On : 01-16-2025 |
| 8 | +*********************************************************************** |
| 9 | +<summary>Chatbot component to interact with Azure OpenAI</summary> |
| 10 | +*********************************************************************** |
| 11 | +*@ |
| 12 | +@rendermode InteractiveServer |
2 | 13 |
|
3 | 14 | @inject IJSRuntime JsRuntime
|
4 | 15 | @using Azure
|
|
9 | 20 | @using Microsoft.SemanticKernel.ChatCompletion;
|
10 | 21 | @using Microsoft.SemanticKernel.Connectors.OpenAI;
|
11 | 22 |
|
| 23 | +@* when checkbox is checked, the chatbot appears, otherwise it clears chat history and hides *@ |
12 | 24 | <input type="checkbox" id="check" @onclick="this.ClearChat" />
|
13 | 25 | <label class="chat-btn" for="check">
|
14 | 26 | <i class="fa fa-commenting-o comment"></i>
|
15 | 27 | <i class="fa fa-close close"></i>
|
16 | 28 | </label>
|
| 29 | + |
| 30 | +@* the chatbot ui *@ |
17 | 31 | <div class="wrapper">
|
18 | 32 | <div class="container">
|
19 | 33 | <div class="d-flex justify-content-center">
|
|
22 | 36 | <p class="mb-0 fw-bold">Chat with Azure OpenAI</p>
|
23 | 37 | </div>
|
24 | 38 | <div class="card-body" id="messages-container" style="height: 500px; overflow-y: auto; background-color: #eee; border: 2px solid #0CCAF0">
|
| 39 | + @* display each message in history *@ |
25 | 40 | @foreach (string chatMessage in this.messages)
|
26 | 41 | {
|
27 | 42 | @if (chatMessage.Contains("|AI|"))
|
28 | 43 | {
|
| 44 | + @* AI message *@ |
29 | 45 | <div class="d-flex flex-row justify-content-start mb-4">
|
30 | 46 | <div class="avatarSticky">
|
31 | 47 | <img src="openai-chatgpt-logo-icon.webp" alt="avatar 1" style="width: 45px; height: 100%;">
|
|
37 | 53 | continue;
|
38 | 54 | }
|
39 | 55 |
|
| 56 | + @* User message *@ |
40 | 57 | <div class="d-flex flex-row justify-content-end mb-4">
|
41 | 58 | <div class="p-3 me-3 border" style="border-radius: 15px; background-color: #fbfbfb;">
|
42 | 59 | @((MarkupString)chatMessage)
|
|
47 | 64 | </div>
|
48 | 65 | }
|
49 | 66 |
|
| 67 | + @* display the streaming response *@ |
50 | 68 | @if (!string.IsNullOrEmpty(this.htmlStreamingResponse))
|
51 | 69 | {
|
52 | 70 | <div class="d-flex flex-row justify-content-start mb-4">
|
|
59 | 77 | </div>
|
60 | 78 | }
|
61 | 79 | </div>
|
| 80 | + |
| 81 | + @* the form for user to ask question *@ |
62 | 82 | <div class="card-footer bg-info text-white" style="border-bottom-left-radius: 15px; border-bottom-right-radius: 15px;">
|
63 | 83 | <div class="input-group">
|
64 | 84 | <input type="text" class="form-control" placeholder="Prompt..." @bind="this.message" id="Message" @onkeydown="this.EnterCheckMessage" disabled=@(!string.IsNullOrEmpty(this.htmlStreamingResponse)) />
|
|
74 | 94 | </div>
|
75 | 95 | </div>
|
76 | 96 |
|
| 97 | +@* Include this javascript on the head section of html to be used by the chatbot *@ |
77 | 98 | <HeadContent>
|
78 | 99 | <script>
|
79 | 100 | function scrollToBottom() {
|
80 | 101 | var objDiv = document.getElementById("messages-container");
|
81 | 102 | objDiv.scrollTop = objDiv.scrollHeight;
|
82 | 103 | }
|
83 | 104 |
|
84 |
| - function focusOnWhatAmI() { |
85 |
| - var what = document.getElementById("WhatAmI"); |
86 |
| - if (what != null) { |
87 |
| - setTimeout(() => what.focus(), 100); |
88 |
| - } |
89 |
| - } |
90 |
| -
|
91 | 105 | function focusOnMessage() {
|
92 | 106 | var message = document.getElementById("Message");
|
93 | 107 | if (message != null) {
|
|
97 | 111 | </script>
|
98 | 112 | </HeadContent>
|
99 | 113 |
|
| 114 | +@* ReSharper disable once CSharpWarnings::CS8618 *@ |
100 | 115 | @code {
|
101 | 116 | #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.
|
102 | 117 |
|
|
118 | 133 | private string htmlStreamingResponse = string.Empty;
|
119 | 134 | private readonly List<string> messages = [];
|
120 | 135 |
|
| 136 | + #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously |
121 | 137 | protected override async Task OnInitializedAsync()
|
| 138 | + #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously |
122 | 139 | {
|
| 140 | + // Get Azure OpenAI settings |
123 | 141 | string aoaiKey = this.Configuration["AzureOpenAI:Key"] ?? throw new Exception("AzureOpenAI:Key needs to be set"); string aoaiEndpoint = this.Configuration["AzureOpenAI:Endpoint"] ?? throw new Exception("AzureOpenAI:Endpoint needs to be set");
|
124 | 142 | string aoaiChatDeploymentName = this.Configuration["AzureOpenAI:ChatDeploymentName"] ?? throw new Exception("AzureOpenAI:ChatDeploymentName needs to be set");
|
125 | 143 | string aoaiEmbeddingDeploymentName = this.Configuration["AzureOpenAI:EmbeddingDeploymentName"] ?? throw new Exception("AzureOpenAI:EmbeddingDeploymentName needs to be set");
|
|
144 | 162 | // Finalize Kernel Builder
|
145 | 163 | this.kernel = kernelBuilder.Build();
|
146 | 164 |
|
147 |
| - // Add Search Plugin |
148 |
| - this.kernel.Plugins.AddFromType<SearchPlugin>("SearchPlugin", this.kernel.Services); |
149 |
| - |
150 |
| - // Add Time Information Plugin |
151 |
| - this.kernel.Plugins.AddFromType<TimeInformationPlugin>(); |
| 165 | + // Add Plugins |
| 166 | + this.kernel.Plugins.AddFromType<SearchPlugin>(nameof(SearchPlugin), this.kernel.Services); |
| 167 | + this.kernel.Plugins.AddFromType<TimeInformationPlugin>(nameof(TimeInformationPlugin), this.kernel.Services); |
152 | 168 |
|
153 | 169 | // Chat Completion Service
|
154 | 170 | this.chatCompletionService = this.kernel.Services.GetRequiredService<IChatCompletionService>();
|
155 | 171 |
|
156 | 172 | // Create OpenAIPromptExecutionSettings
|
157 | 173 | this.openAIPromptExecutionSettings = new OpenAIPromptExecutionSettings
|
158 |
| - { |
159 |
| - ChatSystemPrompt = "You are an HR virtual assistant at Contoso Hotels, expert at answering employee questions related to privacy policy, vacation policy, and describe a few job roles. Ask followup questions if something is unclear or more data is needed to complete a task.", |
160 |
| - Temperature = 0.9, // Set the temperature to 0.9 |
161 |
| - FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() // Auto invoke kernel functions |
162 |
| - }; |
163 |
| - } |
164 |
| - |
165 |
| - protected override async Task OnAfterRenderAsync(bool firstRender) |
166 |
| - { |
167 |
| - if (firstRender) |
168 | 174 | {
|
169 |
| - this.StateHasChanged(); |
170 |
| - } |
171 |
| - |
172 |
| - await base.OnAfterRenderAsync(firstRender); |
| 175 | + ChatSystemPrompt = "You are an HR virtual assistant at Contoso Hotels, expert at answering employee questions related to privacy policy, vacation policy, and describe a few job roles. Ask followup questions if something is unclear or more data is needed to complete a task. You can also answer generic questions about the world as it is a global company.", |
| 176 | + Temperature = 0.9, // Set the temperature to 0.9 |
| 177 | + FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() // Auto invoke kernel functions |
| 178 | + }; |
173 | 179 | }
|
174 | 180 |
|
| 181 | + // Check if the user pressed Enter key |
175 | 182 | private async Task EnterCheckMessage(KeyboardEventArgs e)
|
176 | 183 | {
|
177 | 184 | if (e.Key == "Enter")
|
|
180 | 187 | }
|
181 | 188 | }
|
182 | 189 |
|
| 190 | + // Send chat message to the AI |
183 | 191 | private async Task SendChat()
|
184 | 192 | {
|
185 | 193 | if (string.IsNullOrEmpty(this.message))
|
|
211 | 219 | await this.JsRuntime.InvokeVoidAsync("focusOnMessage");
|
212 | 220 | }
|
213 | 221 |
|
| 222 | + // Clear the chat |
214 | 223 | private async Task ClearChat()
|
215 | 224 | {
|
216 | 225 | this.messages.Clear();
|
217 | 226 | this.chatHistory.Clear();
|
218 |
| - this.chatHistory.AddSystemMessage("You are an HR virtual assistant at Contoso Hotels, expert at answering employee questions related to privacy policy, vacation policy, and describe a few job roles. Ask followup questions if something is unclear or more data is needed to complete a task."); |
| 227 | + this.chatHistory.AddSystemMessage("You are an HR virtual assistant at Contoso Hotels, expert at answering employee questions related to privacy policy, vacation policy, and describe a few job roles. Ask followup questions if something is unclear or more data is needed to complete a task. You can also answer generic questions about the world as it is a global company."); |
219 | 228 | this.htmlStreamingResponse = string.Empty;
|
220 | 229 | this.message = string.Empty;
|
221 | 230 | this.messages.Add("|AI|Welcome to the AI chatbot! I am an AI chatbot trained by OpenAI.");
|
|
0 commit comments