This commit is contained in:
jx 2023-04-06 16:16:20 +08:00
parent fbc820208a
commit 7626775d4b
8 changed files with 82 additions and 16 deletions

10
GptBlazor/Constant.cs Normal file
View File

@ -0,0 +1,10 @@
namespace GptBlazor;
public class Constant
{
public static string? ResourceName;
public static string? DeploymentId;
public static string? ApiKey;
}

View File

@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>c76d0bd7-b6fa-4aaa-baeb-49ab35f36fc4</UserSecretsId>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -12,6 +13,7 @@
<PackageReference Include="Furion" Version="4.8.7.35" /> <PackageReference Include="Furion" Version="4.8.7.35" />
<PackageReference Include="Markdig" Version="0.31.0" /> <PackageReference Include="Markdig" Version="0.31.0" />
<PackageReference Include="OpenAI" Version="1.7.2" /> <PackageReference Include="OpenAI" Version="1.7.2" />
<PackageReference Include="Toolbelt.Blazor.HotKeys2" Version="1.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -14,7 +14,7 @@
<div class="card-header d-flex justify-content-between align-items-center p-3"> <div class="card-header d-flex justify-content-between align-items-center p-3">
<h5 class="mb-0">Chat</h5> <h5 class="mb-0">Chat</h5>
</div> </div>
<div class="card-body" style="overflow: auto; position: relative; height: calc(100vh - 196px)"> <div class="card-body" id="scroll" style="overflow: auto; position: relative; height: calc(100vh - 196px)">
@foreach (var message in _messages) @foreach (var message in _messages)
{ {
@ -57,8 +57,8 @@
<div class="card-footer text-muted d-flex justify-content-start align-items-center p-3"> <div class="card-footer text-muted d-flex justify-content-start align-items-center p-3">
<img src="https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-chat/ava3-bg.webp" <img src="https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-chat/ava3-bg.webp"
alt="avatar 3" style="width: 40px; height: 100%;"> alt="avatar 3" style="width: 40px; height: 100%;">
<Textarea rows="4" @bind-Value="@_userInput"></Textarea> <textarea @ref="Input" rows="4" @bind-value="@_userInput" @bind-value:event="onchange" class="form-control"></textarea>
<Button Icon="fas fa-paper-plane" OnClick="@SendMessage"></Button> <Button @ref="SubmitButton" Icon="fas fa-paper-plane" Text="发送[Ctrl+Enter]" OnClick="@SendMessage" IsDisabled="_isThinking"></Button>
</div> </div>
</div> </div>

View File

@ -1,35 +1,59 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using BootstrapBlazor.Components;
using Furion.LinqBuilder; using Furion.LinqBuilder;
using GptBlazor.Entity;
using GptBlazor.Service; using GptBlazor.Service;
using Markdig; using Markdig;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Toolbelt.Blazor.HotKeys2;
using Message = GptBlazor.Entity.Message;
namespace GptBlazor.Pages; namespace GptBlazor.Pages;
public partial class Index public partial class Index : IAsyncDisposable
{ {
private List<Message> _messages; private List<Message> _messages;
[Inject]
[NotNull]
private IJSRuntime? JsRuntime { get; set; }
[Inject] [Inject]
[NotNull] [NotNull]
private OpenAiService? OpenAiService { get; set; } private OpenAiService? OpenAiService { get; set; }
[Inject]
[NotNull]
private HotKeys HotKeys { get; set; }
private ElementReference Input { get; set; }
private Button? SubmitButton { get; set; }
private string _userInput = string.Empty; private string _userInput = string.Empty;
private string _response = string.Empty; private string _response = string.Empty;
private bool _isThinking = false; private bool _isThinking = false;
protected override async Task OnInitializedAsync() private HotKeysContext HotKeysContext;
protected override void OnInitialized()
{ {
await base.OnInitializedAsync(); base.OnInitialized();
_messages = new List<Message>(); _messages = new List<Message>();
this.HotKeysContext = this.HotKeys.CreateContext()
.Add(ModCode.Ctrl, Code.Enter, async () =>
{
await SubmitButton.FocusAsync();
await SendMessage();
}, exclude: Exclude.None);
} }
private async Task SendMessage() private async Task SendMessage()
{ {
if (_userInput.IsNullOrEmpty()) //await JsRuntime.InvokeVoidAsync("focusInput", SubmitButton);
if (_userInput.IsNullOrEmpty() || _isThinking)
{ {
return; return;
} }
@ -44,10 +68,12 @@ public partial class Index
}); });
_isThinking = true; _isThinking = true;
StateHasChanged(); StateHasChanged();
await JsRuntime.InvokeVoidAsync("scrollToBottom");
await foreach (var str in OpenAiService.StreamResponseEnumerableFromChatbotAsync(input)) await foreach (var str in OpenAiService.StreamResponseEnumerableFromChatbotAsync(input))
{ {
_response += str; _response += str;
StateHasChanged(); StateHasChanged();
await JsRuntime.InvokeVoidAsync("scrollToBottom");
} }
_messages.Add(new Message _messages.Add(new Message
@ -59,5 +85,12 @@ public partial class Index
_isThinking = false; _isThinking = false;
_response = string.Empty; _response = string.Empty;
StateHasChanged(); StateHasChanged();
await JsRuntime.InvokeVoidAsync("scrollToBottom");
await Input.FocusAsync();
}
public async ValueTask DisposeAsync()
{
await this.HotKeys.DisposeAsync();
} }
} }

View File

@ -34,5 +34,6 @@
<script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script> <script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script>
<script src="_framework/blazor.server.js"></script> <script src="_framework/blazor.server.js"></script>
<script src="js/prism.js"></script> <script src="js/prism.js"></script>
<script src="js/site.js"></script>
</body> </body>
</html> </html>

View File

@ -1,6 +1,10 @@
using GptBlazor;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web;
using GptBlazor.Data; using GptBlazor.Data;
using Microsoft.Extensions.Primitives;
using Toolbelt.Blazor.Extensions.DependencyInjection;
using App = Furion.App;
var builder = WebApplication.CreateBuilder(args).Inject(); var builder = WebApplication.CreateBuilder(args).Inject();
@ -9,6 +13,19 @@ builder.Services.AddRazorPages().AddInjectBase();
builder.Services.AddServerSideBlazor(); builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>(); builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddBootstrapBlazor(); builder.Services.AddBootstrapBlazor();
builder.Services.AddHotKeys2();
var config = App.Configuration.GetSection("ChatGpt");
Constant.ResourceName = config["ResourceName"];
Constant.DeploymentId = config["DeploymentId"];
Constant.ApiKey = config["ApiKey"];
ChangeToken.OnChange(() => config.GetReloadToken(), () =>
{
Constant.ResourceName = config["ResourceName"];
Constant.DeploymentId = config["DeploymentId"];
Constant.ApiKey = config["ApiKey"];
});
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.

View File

@ -6,17 +6,12 @@ namespace GptBlazor.Service;
public class OpenAiService : IScoped public class OpenAiService : IScoped
{ {
private const string _resourceName = "jxgpt";
private const string _deploymentId = "gpt35test";
private const string _apiKey = "e97ae651d99945e2b3bb7e666442f5e9";
private readonly Conversation conversation; private readonly Conversation conversation;
public OpenAiService() public OpenAiService()
{ {
var api = OpenAIAPI.ForAzure(_resourceName, _deploymentId, _apiKey); var api = OpenAIAPI.ForAzure(Constant.ResourceName, Constant.DeploymentId, Constant.ApiKey);
api.ApiVersion = "2023-03-15-preview"; api.ApiVersion = "2023-03-15-preview";
conversation = api.Chat.CreateConversation(); conversation = api.Chat.CreateConversation();
} }

View File

@ -0,0 +1,8 @@
function scrollToBottom() {
var element = document.getElementById("scroll");
element.scrollTop = element.scrollHeight;
}
function focusInput(element) {
element.focus();
}