From 3f4dff14db436c55acbec187e2e7afe49f79c791 Mon Sep 17 00:00:00 2001 From: Erin McLaughlin Date: Thu, 18 Apr 2024 18:58:53 -0400 Subject: [PATCH 1/7] refactored chat stuff into Aibba class --- src/Site/Site/AI/AIOptions.cs | 9 +- src/Site/Site/AI/Aibba.cs | 76 +++++++++++++++++ .../AI/{WebsitePlugin.cs => AibbaPlugin.cs} | 2 +- src/Site/Site/Components/Home.razor | 6 +- src/Site/Site/Components/Home.razor.cs | 37 +-------- .../Site/Components/TerminalChat.razor.cs | 82 +++++-------------- src/Site/Site/Program.cs | 2 + 7 files changed, 113 insertions(+), 101 deletions(-) create mode 100644 src/Site/Site/AI/Aibba.cs rename src/Site/Site/AI/{WebsitePlugin.cs => AibbaPlugin.cs} (94%) diff --git a/src/Site/Site/AI/AIOptions.cs b/src/Site/Site/AI/AIOptions.cs index 5c0c619..f7bcb07 100644 --- a/src/Site/Site/AI/AIOptions.cs +++ b/src/Site/Site/AI/AIOptions.cs @@ -1,4 +1,6 @@ -namespace Site.AI; +using Microsoft.SemanticKernel; + +namespace Site.AI; internal sealed class AIOptions { @@ -6,4 +8,9 @@ internal sealed class AIOptions public bool Enabled { get; set; } public string TextCompletionModel { get; set; } = string.Empty; public string TextEmbeddingModel { get; set; } = string.Empty; + + public Kernel BuildDefaultKernel() => Kernel.CreateBuilder() + .AddOpenAIChatCompletion(TextCompletionModel, ApiKey) + .AddOpenAITextEmbeddingGeneration(TextEmbeddingModel, ApiKey) + .Build(); } diff --git a/src/Site/Site/AI/Aibba.cs b/src/Site/Site/AI/Aibba.cs new file mode 100644 index 0000000..e5a3d36 --- /dev/null +++ b/src/Site/Site/AI/Aibba.cs @@ -0,0 +1,76 @@ +using Microsoft.Extensions.Options; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.ChatCompletion; +using Microsoft.SemanticKernel.Connectors.OpenAI; +using Site.Components; +using System.Diagnostics.CodeAnalysis; + +namespace Site.AI; + +internal sealed class Aibba +{ + private readonly Kernel _kernel; + private readonly ChatHistory _messages = []; + private readonly OpenAIPromptExecutionSettings _promptExecutionSettings = new(); + private readonly Queue _queue = []; + + public Aibba(IOptions options, AibbaPlugin plugin) + { + _kernel = options.Value.BuildDefaultKernel(); + _kernel.Plugins.AddFromObject(plugin); + + _promptExecutionSettings.ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions; + + InitializeChatHistory(); + } + + public Task TriggerResponse(string message, CancellationToken cancellationToken = default) + { + AddUserMessage(message); + return TriggerResponse(cancellationToken); + } + + private async Task TriggerResponse(CancellationToken cancellationToken) + { + var chatService = _kernel.GetRequiredService(); + var chatMessageContent = await chatService.GetChatMessageContentAsync(_messages, _promptExecutionSettings, _kernel, cancellationToken); + AddAibbaMessage(chatMessageContent.Content ?? string.Empty); + } + + public bool TryGetNextMessage([NotNullWhen(true)] out ChatMessage? message) + { + return _queue.TryDequeue(out message); + } + + private void InitializeChatHistory() + { + _messages.AddSystemMessage(""" + You are an AI assistant named Aibba and you are running on Erin McLaughlin's personal website. + Erin is the software engineer that programmed you. Talk to users about her. + """); + + AddErinMessage("Hi! Welcome to my website. I'm a software engineer with a passion for building context-driven systems."); + AddErinMessage("You can check out my work on [GitHub](https://github.com/erinnmclaughlin), or ask my AI friend Aibba about me!"); + AddErinMessage("Aibba is a large language model I've integrated into my website to answer questions you might have about me."); + AddErinMessage("Alright - I gotta go! Aibba, can you take it from here?"); + + AddAibbaMessage("Sure thing, Erin! Feel free to ask me if there's anything else you'd like to know about Erin!"); + } + + private void AddAibbaMessage(string message) + { + _messages.AddMessage(AuthorRole.Assistant, message); + _queue.Enqueue(new("Aibba") { Content = message }); + } + + private void AddErinMessage(string message) + { + _messages.AddMessage(AuthorRole.System, message); + _queue.Enqueue(new("Erin") { Content = message }); + } + + private void AddUserMessage(string message) + { + _messages.AddMessage(AuthorRole.User, message); + } +} diff --git a/src/Site/Site/AI/WebsitePlugin.cs b/src/Site/Site/AI/AibbaPlugin.cs similarity index 94% rename from src/Site/Site/AI/WebsitePlugin.cs rename to src/Site/Site/AI/AibbaPlugin.cs index 59d0b2f..50420de 100644 --- a/src/Site/Site/AI/WebsitePlugin.cs +++ b/src/Site/Site/AI/AibbaPlugin.cs @@ -4,7 +4,7 @@ namespace Site.AI; -public class WebsitePlugin(NavigationManager navigationManager) +public sealed class AibbaPlugin(NavigationManager navigationManager) { [KernelFunction, Description("Get a link to Erin's GitHub profile.")] public static string GetGitHubUrl() => "https://github.com/erinnmclaughlin"; diff --git a/src/Site/Site/Components/Home.razor b/src/Site/Site/Components/Home.razor index a3fd363..b36c343 100644 --- a/src/Site/Site/Components/Home.razor +++ b/src/Site/Site/Components/Home.razor @@ -21,12 +21,12 @@ - @if (Kernel is null) + @if (ShowAibbaChat) { - + } else { - + } diff --git a/src/Site/Site/Components/Home.razor.cs b/src/Site/Site/Components/Home.razor.cs index 70bd7e4..9f97c7b 100644 --- a/src/Site/Site/Components/Home.razor.cs +++ b/src/Site/Site/Components/Home.razor.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Options; -using Microsoft.SemanticKernel; using Site.AI; using System.Diagnostics.CodeAnalysis; namespace Site.Components; -public sealed partial class Home : IDisposable +public sealed partial class Home { [Parameter, SupplyParameterFromQuery] public bool Preview { get; set; } @@ -14,38 +13,10 @@ public sealed partial class Home : IDisposable [Inject, NotNull] private IOptionsMonitor? AIOptionsMonitor { get; set; } - [Inject, NotNull] - private NavigationManager? NavigationManager { get; set; } - - private IDisposable? AIOptionsSubscription { get; set; } - private Kernel? Kernel { get; set; } - - public void Dispose() - { - AIOptionsSubscription?.Dispose(); - } - - protected override void OnInitialized() - { - UpdateAIOptions(AIOptionsMonitor.CurrentValue); - AIOptionsSubscription = AIOptionsMonitor.OnChange(UpdateAIOptions); - } + private bool ShowAibbaChat { get; set; } - private void UpdateAIOptions(AIOptions options) + protected override void OnParametersSet() { - Kernel = null; - - if (Preview || options.Enabled) - { - var kernelBuilder = Kernel.CreateBuilder() - .AddOpenAIChatCompletion(options.TextCompletionModel, options.ApiKey) - .AddOpenAITextEmbeddingGeneration(options.TextEmbeddingModel, options.ApiKey); - - kernelBuilder.Plugins.AddFromObject(new WebsitePlugin(NavigationManager)); - - Kernel = kernelBuilder.Build(); - } - - InvokeAsync(StateHasChanged); + ShowAibbaChat = Preview && AIOptionsMonitor.CurrentValue.Enabled; } } diff --git a/src/Site/Site/Components/TerminalChat.razor.cs b/src/Site/Site/Components/TerminalChat.razor.cs index 115eb8b..af9b127 100644 --- a/src/Site/Site/Components/TerminalChat.razor.cs +++ b/src/Site/Site/Components/TerminalChat.razor.cs @@ -1,7 +1,5 @@ using Microsoft.AspNetCore.Components; -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.ChatCompletion; -using Microsoft.SemanticKernel.Connectors.OpenAI; +using Site.AI; namespace Site.Components; @@ -14,89 +12,54 @@ public class ChatMessage(string author) public sealed partial class TerminalChat { private readonly CancellationTokenSource _ctSource = new(); - private readonly OpenAIPromptExecutionSettings _promptExecutionSettings = new() { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions }; - [Parameter, EditorRequired] - public required Kernel Kernel { get; set; } - [Inject] - private NavigationManager NavigationManager { get; set; } = default!; + private Aibba Aibba { get; set; } = default!; - private ChatHistory ChatHistory { get; } = []; private List Messages { get; } = []; private ChatState State { get; set; } = ChatState.StreamingMessage; protected override void OnInitialized() { - ChatHistory.AddSystemMessage(""" - You are an AI assistant named Aibba and you are running on Erin McLaughlin's personal website. - Erin is the software engineer that programmed you. Talk to users about her. - """); - - InvokeAsync(RenderGreeting); - } - - private async Task RenderGreeting() - { - foreach (var message in WelcomeMessages) - { - await RenderGreeting(message); - await Task.Delay(300); - } - - await RenderAssistantMessage("Sure thing! Feel free to ask me if there's anything else you'd like to know about Erin!"); + InvokeAsync(RenderQueuedMessages); } - private async Task RenderGreeting(string message) + private async Task RenderQueuedMessages() { - var chatMessage = new ChatMessage("Erin") { Content = string.Empty }; - Messages.Add(chatMessage); + State = ChatState.StreamingMessage; + StateHasChanged(); - foreach (var part in message) + while (Aibba.TryGetNextMessage(out var message)) { - chatMessage.Content += part; - await Task.Delay(30); - StateHasChanged(); + await RenderMessage(message); + await Task.Delay(300, _ctSource.Token); } - ChatHistory.AddSystemMessage(message); + State = ChatState.WaitingForUser; + StateHasChanged(); } private async Task RenderUserMessage(string message) { - ChatHistory.AddUserMessage(message); - Messages.Add(new ("User") { Content = message }); - - await RenderAssistantMessage(); - } - - private async Task RenderAssistantMessage(string? message = null) - { + Messages.Add(new ChatMessage("User") { Content = message }); State = ChatState.WaitingForAssistant; StateHasChanged(); - if (message is null) - { - var chatService = Kernel.GetRequiredService(); - var chatMessageContent = await chatService.GetChatMessageContentAsync(ChatHistory, _promptExecutionSettings, Kernel, _ctSource.Token); - message = chatMessageContent.Content ?? string.Empty; - } + await Aibba.TriggerResponse(message, _ctSource.Token); + await RenderQueuedMessages(); + } - var chatMessage = new ChatMessage("Aibba") { Content = string.Empty }; + private async Task RenderMessage(ChatMessage message) + { + var chatMessage = new ChatMessage(message.Author) { Content = string.Empty }; Messages.Add(chatMessage); - State = ChatState.StreamingMessage; - - foreach (var part in message) + foreach (var part in message.Content ?? "") { chatMessage.Content += part; await Task.Delay(30); StateHasChanged(); } - - ChatHistory.AddAssistantMessage(chatMessage.Content); - State = ChatState.WaitingForUser; - StateHasChanged(); } private enum ChatState @@ -106,11 +69,4 @@ private enum ChatState StreamingMessage, WaitingForUser } - - private readonly List WelcomeMessages = [ - "Hi! Welcome to my website. I'm a software engineer with a passion for building context-driven systems.", - "You can check out my work on [GitHub](https://github.com/erinnmclaughlin), or ask my AI friend Aibba about me!", - "Aibba is a large language model I've integrated into my website to answer questions you might have about me.", - "Alright - I gotta go! Aibba, can you take it from here?" - ]; } \ No newline at end of file diff --git a/src/Site/Site/Program.cs b/src/Site/Site/Program.cs index 3176226..a7bf979 100644 --- a/src/Site/Site/Program.cs +++ b/src/Site/Site/Program.cs @@ -8,6 +8,8 @@ .AddInteractiveWebAssemblyComponents(); builder.Services.Configure(builder.Configuration.GetSection("AI")); +builder.Services.AddScoped(); +builder.Services.AddScoped(); var app = builder.Build(); From 81479b89acab717a242482f425d28e2edf85c02f Mon Sep 17 00:00:00 2001 From: Erin McLaughlin Date: Thu, 18 Apr 2024 20:29:59 -0400 Subject: [PATCH 2/7] signalr hub --- .../Components/TerminalChat.razor | 15 ++-- .../Components/TerminalChat.razor.cs | 88 +++++++++++++++++++ .../Components/TerminalChat.razor.css | 0 .../Components/TerminalChatContent.razor | 22 +++++ ...VeryRealisticAnimationOfMyselfAndJax.razor | 4 +- src/Site/Site.Client/Pages/Counter.razor | 19 ---- .../Pages}/Home.razor | 11 ++- .../Pages}/Home.razor.css | 0 src/Site/Site.Client/Site.Client.csproj | 3 +- src/Site/Site.Client/TerminalChatMessage.cs | 7 ++ src/Site/Site.Client/_Imports.razor | 1 + src/Site/Site/AI/AIOptions.cs | 2 +- src/Site/Site/AI/Aibba.cs | 20 +++-- src/Site/Site/AI/AibbaHub.cs | 69 +++++++++++++++ src/Site/Site/Components/Home.razor.cs | 22 ----- .../Site/Components/TerminalChat.razor.cs | 72 --------------- src/Site/Site/Program.cs | 10 +++ 17 files changed, 232 insertions(+), 133 deletions(-) rename src/Site/{Site => Site.Client}/Components/TerminalChat.razor (66%) create mode 100644 src/Site/Site.Client/Components/TerminalChat.razor.cs rename src/Site/{Site => Site.Client}/Components/TerminalChat.razor.css (100%) create mode 100644 src/Site/Site.Client/Components/TerminalChatContent.razor delete mode 100644 src/Site/Site.Client/Pages/Counter.razor rename src/Site/{Site/Components => Site.Client/Pages}/Home.razor (81%) rename src/Site/{Site/Components => Site.Client/Pages}/Home.razor.css (100%) create mode 100644 src/Site/Site.Client/TerminalChatMessage.cs create mode 100644 src/Site/Site/AI/AibbaHub.cs delete mode 100644 src/Site/Site/Components/Home.razor.cs delete mode 100644 src/Site/Site/Components/TerminalChat.razor.cs diff --git a/src/Site/Site/Components/TerminalChat.razor b/src/Site/Site.Client/Components/TerminalChat.razor similarity index 66% rename from src/Site/Site/Components/TerminalChat.razor rename to src/Site/Site.Client/Components/TerminalChat.razor index fe14cdb..7e4341a 100644 --- a/src/Site/Site/Components/TerminalChat.razor +++ b/src/Site/Site.Client/Components/TerminalChat.razor @@ -1,23 +1,20 @@ -@rendermode InteractiveServer +@rendermode InteractiveAuto @attribute [StreamRendering] -
+ +
@foreach (var message in Messages) {
- +

@message.Message

} - @if (State is ChatState.Loading) - { -

Loading...

- } - else if (State is ChatState.WaitingForAssistant) + @if (State is ChatState.WaitingForAssistant) {

Thinking...

} @@ -25,4 +22,4 @@ { } -
\ No newline at end of file +
diff --git a/src/Site/Site.Client/Components/TerminalChat.razor.cs b/src/Site/Site.Client/Components/TerminalChat.razor.cs new file mode 100644 index 0000000..456a371 --- /dev/null +++ b/src/Site/Site.Client/Components/TerminalChat.razor.cs @@ -0,0 +1,88 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.SignalR.Client; +using System.Diagnostics.CodeAnalysis; + +namespace Site.Client.Components; + +public sealed partial class TerminalChat : IAsyncDisposable +{ + private HubConnection? _hubConnection; + + [Inject, NotNull] + private NavigationManager? NavigationManager { get; set; } + + private List Messages { get; } = []; + + private ChatState State { get; set; } + + public async ValueTask DisposeAsync() + { + if (_hubConnection is not null) + await _hubConnection.DisposeAsync(); + } + + protected override void OnInitialized() + { + _hubConnection = new HubConnectionBuilder() + .WithUrl(NavigationManager.ToAbsoluteUri("/aibba")) + .Build(); + + _hubConnection.On>("ReceiveMessage", (chatMessages) => + { + InvokeAsync(async () => await RenderReceivedMessagesAsync(chatMessages)); + }); + + InvokeAsync(() => _hubConnection.StartAsync()); + } + + private async Task RenderUserMessage(string message) + { + State = ChatState.WaitingForAssistant; + Messages.Add(new TerminalChatMessage { Author = "User", Message = message }); + StateHasChanged(); + + if (_hubConnection is not null) + await _hubConnection.SendAsync("SendMessage", message); + } + + private async Task RenderReceivedMessagesAsync(List messages) + { + State = ChatState.Rendering; + StateHasChanged(); + + for (int i = 0; i < messages.Count; i++) + { + await RenderReceivedMessageAsync(messages[i]); + + if (i != messages.Count - 1) + await Task.Delay(300); + } + + State = ChatState.WaitingForUser; + StateHasChanged(); + } + + private async Task RenderReceivedMessageAsync(TerminalChatMessage chatContent) + { + var message = new TerminalChatMessage + { + Author = chatContent.Author + }; + + Messages.Add(message); + + foreach (var c in chatContent.Message) + { + message.Message += c; + await Task.Delay(30); + StateHasChanged(); + } + } + + enum ChatState + { + Rendering, + WaitingForAssistant, + WaitingForUser + } +} \ No newline at end of file diff --git a/src/Site/Site/Components/TerminalChat.razor.css b/src/Site/Site.Client/Components/TerminalChat.razor.css similarity index 100% rename from src/Site/Site/Components/TerminalChat.razor.css rename to src/Site/Site.Client/Components/TerminalChat.razor.css diff --git a/src/Site/Site.Client/Components/TerminalChatContent.razor b/src/Site/Site.Client/Components/TerminalChatContent.razor new file mode 100644 index 0000000..dc49f5e --- /dev/null +++ b/src/Site/Site.Client/Components/TerminalChatContent.razor @@ -0,0 +1,22 @@ +@*

@_renderedContent

+ +@code { + + private string _renderedContent = string.Empty; + + [Parameter] + public CancellationTokenSource? CancellationTokenSource { get; set; } + + [Parameter] + public string? Content { get; set; } + + [Parameter] + public EventCallback OnRenderingCompleted { get; set; } + + protected override void OnInitialized() + { + InvokeAsync(RenderMessageAsync); + } + +} + *@ \ No newline at end of file diff --git a/src/Site/Site.Client/Components/VeryRealisticAnimationOfMyselfAndJax.razor b/src/Site/Site.Client/Components/VeryRealisticAnimationOfMyselfAndJax.razor index f4d5400..c8563d1 100644 --- a/src/Site/Site.Client/Components/VeryRealisticAnimationOfMyselfAndJax.razor +++ b/src/Site/Site.Client/Components/VeryRealisticAnimationOfMyselfAndJax.razor @@ -1,4 +1,6 @@ -
+ + +
z
z
diff --git a/src/Site/Site.Client/Pages/Counter.razor b/src/Site/Site.Client/Pages/Counter.razor deleted file mode 100644 index e7d4f94..0000000 --- a/src/Site/Site.Client/Pages/Counter.razor +++ /dev/null @@ -1,19 +0,0 @@ -@page "/counter" -@rendermode InteractiveAuto - -Counter - -

Counter

- -

Current count: @currentCount

- - - -@code { - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } -} diff --git a/src/Site/Site/Components/Home.razor b/src/Site/Site.Client/Pages/Home.razor similarity index 81% rename from src/Site/Site/Components/Home.razor rename to src/Site/Site.Client/Pages/Home.razor index b36c343..189192a 100644 --- a/src/Site/Site/Components/Home.razor +++ b/src/Site/Site.Client/Pages/Home.razor @@ -1,5 +1,7 @@ @page "/" -@rendermode InteractiveServer +@rendermode InteractiveAuto + + @@ -21,7 +23,7 @@ - @if (ShowAibbaChat) + @if (Preview) { } @@ -30,3 +32,8 @@ } + +@code { + [Parameter, SupplyParameterFromQuery] + public bool Preview { get; set; } +} diff --git a/src/Site/Site/Components/Home.razor.css b/src/Site/Site.Client/Pages/Home.razor.css similarity index 100% rename from src/Site/Site/Components/Home.razor.css rename to src/Site/Site.Client/Pages/Home.razor.css diff --git a/src/Site/Site.Client/Site.Client.csproj b/src/Site/Site.Client/Site.Client.csproj index f6b3bd7..068256f 100644 --- a/src/Site/Site.Client/Site.Client.csproj +++ b/src/Site/Site.Client/Site.Client.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -11,6 +11,7 @@ + diff --git a/src/Site/Site.Client/TerminalChatMessage.cs b/src/Site/Site.Client/TerminalChatMessage.cs new file mode 100644 index 0000000..33288f4 --- /dev/null +++ b/src/Site/Site.Client/TerminalChatMessage.cs @@ -0,0 +1,7 @@ +namespace Site.Client; + +public class TerminalChatMessage +{ + public string Author { get; set; } = ""; + public string Message { get; set; } = ""; +} diff --git a/src/Site/Site.Client/_Imports.razor b/src/Site/Site.Client/_Imports.razor index 2ccac9a..68c302a 100644 --- a/src/Site/Site.Client/_Imports.razor +++ b/src/Site/Site.Client/_Imports.razor @@ -9,3 +9,4 @@ @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.JSInterop @using Site.Client +@using Site.Client.Components diff --git a/src/Site/Site/AI/AIOptions.cs b/src/Site/Site/AI/AIOptions.cs index f7bcb07..19caf84 100644 --- a/src/Site/Site/AI/AIOptions.cs +++ b/src/Site/Site/AI/AIOptions.cs @@ -2,7 +2,7 @@ namespace Site.AI; -internal sealed class AIOptions +public sealed class AIOptions { public string ApiKey { get; set; } = string.Empty; public bool Enabled { get; set; } diff --git a/src/Site/Site/AI/Aibba.cs b/src/Site/Site/AI/Aibba.cs index e5a3d36..2a8b690 100644 --- a/src/Site/Site/AI/Aibba.cs +++ b/src/Site/Site/AI/Aibba.cs @@ -2,17 +2,17 @@ using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI; -using Site.Components; +using Site.Client; using System.Diagnostics.CodeAnalysis; namespace Site.AI; -internal sealed class Aibba +public sealed class Aibba { private readonly Kernel _kernel; private readonly ChatHistory _messages = []; private readonly OpenAIPromptExecutionSettings _promptExecutionSettings = new(); - private readonly Queue _queue = []; + private readonly Queue _queue = []; public Aibba(IOptions options, AibbaPlugin plugin) { @@ -37,7 +37,15 @@ private async Task TriggerResponse(CancellationToken cancellationToken) AddAibbaMessage(chatMessageContent.Content ?? string.Empty); } - public bool TryGetNextMessage([NotNullWhen(true)] out ChatMessage? message) + public IEnumerable GetNextMessages() + { + while (_queue.TryDequeue(out var message)) + { + yield return message; + } + } + + public bool TryGetNextMessage([NotNullWhen(true)] out TerminalChatMessage? message) { return _queue.TryDequeue(out message); } @@ -60,13 +68,13 @@ Erin is the software engineer that programmed you. Talk to users about her. private void AddAibbaMessage(string message) { _messages.AddMessage(AuthorRole.Assistant, message); - _queue.Enqueue(new("Aibba") { Content = message }); + _queue.Enqueue(new TerminalChatMessage { Author = "Aibba", Message = message }); } private void AddErinMessage(string message) { _messages.AddMessage(AuthorRole.System, message); - _queue.Enqueue(new("Erin") { Content = message }); + _queue.Enqueue(new TerminalChatMessage { Author = "Erin", Message = message }); } private void AddUserMessage(string message) diff --git a/src/Site/Site/AI/AibbaHub.cs b/src/Site/Site/AI/AibbaHub.cs new file mode 100644 index 0000000..e0bc822 --- /dev/null +++ b/src/Site/Site/AI/AibbaHub.cs @@ -0,0 +1,69 @@ +using Microsoft.AspNetCore.SignalR; +using System.Diagnostics.CodeAnalysis; + +namespace Site.AI; + +public sealed class AibbaHub(ILogger logger) : Hub +{ + private readonly Dictionary _aibbas = []; + private readonly ILogger _logger = logger; + + public override async Task OnConnectedAsync() + { + _logger.LogInformation("User connected: {ConnectionId}", Context.ConnectionId); + + if (TryGetAibbaInstance(out var aibba)) + await SendNewMessagesAsync(aibba); + } + + public override Task OnDisconnectedAsync(Exception? exception) + { + _logger.LogInformation("User disconnected: {ConnectionId}", Context.ConnectionId); + _aibbas.Remove(Context.ConnectionId); + return base.OnDisconnectedAsync(exception); + } + + public async Task SendMessage(string message) + { + _logger.LogInformation("User says {message}", message); + + if (TryGetAibbaInstance(out var aibba)) + { + await aibba.TriggerResponse(message, Context.ConnectionAborted); + await SendNewMessagesAsync(aibba); + } + } + + private bool TryGetAibbaInstance([NotNullWhen(true)] out Aibba? aibba) + { + aibba = _aibbas.GetValueOrDefault(Context.ConnectionId); + + if (aibba is null) + { + aibba = Context.GetHttpContext()?.RequestServices.GetRequiredService(); + + if (aibba is null) + { + _logger.LogError("Failed to create Aibba instance for connection {ConnectionId}.", Context.ConnectionId); + return false; + } + + _aibbas.Add(Context.ConnectionId, aibba); + } + + return true; + } + + private async Task SendNewMessagesAsync(Aibba aibba) + { + var messages = aibba.GetNextMessages().ToList(); + + if (messages.Count != 0) + { + foreach (var message in messages) + _logger.LogInformation("Sending message {message} from {author}", message.Message, message.Author); + + await Clients.Caller.SendAsync("ReceiveMessage", messages, Context.ConnectionAborted); + } + } +} diff --git a/src/Site/Site/Components/Home.razor.cs b/src/Site/Site/Components/Home.razor.cs deleted file mode 100644 index 9f97c7b..0000000 --- a/src/Site/Site/Components/Home.razor.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Microsoft.Extensions.Options; -using Site.AI; -using System.Diagnostics.CodeAnalysis; - -namespace Site.Components; - -public sealed partial class Home -{ - [Parameter, SupplyParameterFromQuery] - public bool Preview { get; set; } - - [Inject, NotNull] - private IOptionsMonitor? AIOptionsMonitor { get; set; } - - private bool ShowAibbaChat { get; set; } - - protected override void OnParametersSet() - { - ShowAibbaChat = Preview && AIOptionsMonitor.CurrentValue.Enabled; - } -} diff --git a/src/Site/Site/Components/TerminalChat.razor.cs b/src/Site/Site/Components/TerminalChat.razor.cs deleted file mode 100644 index af9b127..0000000 --- a/src/Site/Site/Components/TerminalChat.razor.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Site.AI; - -namespace Site.Components; - -public class ChatMessage(string author) -{ - public string Author { get; } = author; - public string? Content { get; set; } -} - -public sealed partial class TerminalChat -{ - private readonly CancellationTokenSource _ctSource = new(); - - [Inject] - private Aibba Aibba { get; set; } = default!; - - private List Messages { get; } = []; - private ChatState State { get; set; } = ChatState.StreamingMessage; - - protected override void OnInitialized() - { - InvokeAsync(RenderQueuedMessages); - } - - private async Task RenderQueuedMessages() - { - State = ChatState.StreamingMessage; - StateHasChanged(); - - while (Aibba.TryGetNextMessage(out var message)) - { - await RenderMessage(message); - await Task.Delay(300, _ctSource.Token); - } - - State = ChatState.WaitingForUser; - StateHasChanged(); - } - - private async Task RenderUserMessage(string message) - { - Messages.Add(new ChatMessage("User") { Content = message }); - State = ChatState.WaitingForAssistant; - StateHasChanged(); - - await Aibba.TriggerResponse(message, _ctSource.Token); - await RenderQueuedMessages(); - } - - private async Task RenderMessage(ChatMessage message) - { - var chatMessage = new ChatMessage(message.Author) { Content = string.Empty }; - Messages.Add(chatMessage); - - foreach (var part in message.Content ?? "") - { - chatMessage.Content += part; - await Task.Delay(30); - StateHasChanged(); - } - } - - private enum ChatState - { - Loading, - WaitingForAssistant, - StreamingMessage, - WaitingForUser - } -} \ No newline at end of file diff --git a/src/Site/Site/Program.cs b/src/Site/Site/Program.cs index a7bf979..78f1e54 100644 --- a/src/Site/Site/Program.cs +++ b/src/Site/Site/Program.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.ResponseCompression; using Site.AI; using Site.Components; @@ -11,14 +12,21 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddResponseCompression(o => +{ + o.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(["application/octet-stream"]); +}); + var app = builder.Build(); + if (app.Environment.IsDevelopment()) { app.UseWebAssemblyDebugging(); } else { + app.UseResponseCompression(); app.UseExceptionHandler("/Error", createScopeForErrors: true); app.UseHsts(); } @@ -33,4 +41,6 @@ .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(typeof(Site.Client._Imports).Assembly); +app.MapHub("/aibba"); + app.Run(); From c843ac702d5f62335134a3dc53e3cd3a675e651d Mon Sep 17 00:00:00 2001 From: Erin McLaughlin Date: Thu, 18 Apr 2024 20:37:13 -0400 Subject: [PATCH 3/7] cleanup --- .../Site.Client/Components/TerminalChat.razor | 9 ++++++- .../Components/TerminalChatContent.razor | 24 +++++++++---------- src/Site/Site.Client/Site.Client.csproj | 1 + src/Site/Site/AI/Aibba.cs | 21 ++++------------ src/Site/Site/Components/ChatContent.razor | 20 ---------------- src/Site/Site/Site.csproj | 3 +-- 6 files changed, 26 insertions(+), 52 deletions(-) delete mode 100644 src/Site/Site/Components/ChatContent.razor diff --git a/src/Site/Site.Client/Components/TerminalChat.razor b/src/Site/Site.Client/Components/TerminalChat.razor index 7e4341a..6cfe740 100644 --- a/src/Site/Site.Client/Components/TerminalChat.razor +++ b/src/Site/Site.Client/Components/TerminalChat.razor @@ -10,7 +10,14 @@ -

@message.Message

+ @if (message.Author == "User") + { +

@message.Message

+ } + else + { + + }
} diff --git a/src/Site/Site.Client/Components/TerminalChatContent.razor b/src/Site/Site.Client/Components/TerminalChatContent.razor index dc49f5e..cdda809 100644 --- a/src/Site/Site.Client/Components/TerminalChatContent.razor +++ b/src/Site/Site.Client/Components/TerminalChatContent.razor @@ -1,22 +1,20 @@ -@*

@_renderedContent

+@using Vereyon.Web -@code { - - private string _renderedContent = string.Empty; +@if (Content is { Length: > 0 }) +{ +

+ @Sanitize(Content) +

+} - [Parameter] - public CancellationTokenSource? CancellationTokenSource { get; set; } +@code { + private static readonly HtmlSanitizer _htmlSanitizer = HtmlSanitizer.SimpleHtml5Sanitizer(); [Parameter] public string? Content { get; set; } - [Parameter] - public EventCallback OnRenderingCompleted { get; set; } - - protected override void OnInitialized() + private static MarkupString Sanitize(string html) { - InvokeAsync(RenderMessageAsync); + return new MarkupString((_htmlSanitizer.Sanitize(Markdig.Markdown.ToHtml(html)))); } - } - *@ \ No newline at end of file diff --git a/src/Site/Site.Client/Site.Client.csproj b/src/Site/Site.Client/Site.Client.csproj index 068256f..3ac356c 100644 --- a/src/Site/Site.Client/Site.Client.csproj +++ b/src/Site/Site.Client/Site.Client.csproj @@ -12,6 +12,7 @@ + diff --git a/src/Site/Site/AI/Aibba.cs b/src/Site/Site/AI/Aibba.cs index 2a8b690..1041da9 100644 --- a/src/Site/Site/AI/Aibba.cs +++ b/src/Site/Site/AI/Aibba.cs @@ -3,7 +3,6 @@ using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI; using Site.Client; -using System.Diagnostics.CodeAnalysis; namespace Site.AI; @@ -26,17 +25,10 @@ public Aibba(IOptions options, AibbaPlugin plugin) public Task TriggerResponse(string message, CancellationToken cancellationToken = default) { - AddUserMessage(message); + _messages.AddMessage(AuthorRole.User, message); return TriggerResponse(cancellationToken); } - private async Task TriggerResponse(CancellationToken cancellationToken) - { - var chatService = _kernel.GetRequiredService(); - var chatMessageContent = await chatService.GetChatMessageContentAsync(_messages, _promptExecutionSettings, _kernel, cancellationToken); - AddAibbaMessage(chatMessageContent.Content ?? string.Empty); - } - public IEnumerable GetNextMessages() { while (_queue.TryDequeue(out var message)) @@ -45,11 +37,6 @@ public IEnumerable GetNextMessages() } } - public bool TryGetNextMessage([NotNullWhen(true)] out TerminalChatMessage? message) - { - return _queue.TryDequeue(out message); - } - private void InitializeChatHistory() { _messages.AddSystemMessage(""" @@ -77,8 +64,10 @@ private void AddErinMessage(string message) _queue.Enqueue(new TerminalChatMessage { Author = "Erin", Message = message }); } - private void AddUserMessage(string message) + private async Task TriggerResponse(CancellationToken cancellationToken) { - _messages.AddMessage(AuthorRole.User, message); + var chatService = _kernel.GetRequiredService(); + var chatMessageContent = await chatService.GetChatMessageContentAsync(_messages, _promptExecutionSettings, _kernel, cancellationToken); + AddAibbaMessage(chatMessageContent.Content ?? string.Empty); } } diff --git a/src/Site/Site/Components/ChatContent.razor b/src/Site/Site/Components/ChatContent.razor deleted file mode 100644 index cdda809..0000000 --- a/src/Site/Site/Components/ChatContent.razor +++ /dev/null @@ -1,20 +0,0 @@ -@using Vereyon.Web - -@if (Content is { Length: > 0 }) -{ -

- @Sanitize(Content) -

-} - -@code { - private static readonly HtmlSanitizer _htmlSanitizer = HtmlSanitizer.SimpleHtml5Sanitizer(); - - [Parameter] - public string? Content { get; set; } - - private static MarkupString Sanitize(string html) - { - return new MarkupString((_htmlSanitizer.Sanitize(Markdig.Markdown.ToHtml(html)))); - } -} diff --git a/src/Site/Site/Site.csproj b/src/Site/Site/Site.csproj index 60d88db..b211914 100644 --- a/src/Site/Site/Site.csproj +++ b/src/Site/Site/Site.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -11,7 +11,6 @@ - From d22aaba9f446d3ca041f95c98a7d0b2562ebe2c6 Mon Sep 17 00:00:00 2001 From: Erin McLaughlin Date: Thu, 18 Apr 2024 21:55:05 -0400 Subject: [PATCH 4/7] memories! --- .editorconfig | 3 ++ PersonalWebsite.sln | 1 + src/Site/Site/AI/Aibba.cs | 8 +-- src/Site/Site/AI/AibbaKnowledge.cs | 61 +++++++++++++++++++++++ src/Site/Site/AI/AibbaNavigationPlugin.cs | 33 ++++++++++++ src/Site/Site/AI/AibbaPlugin.cs | 35 ------------- src/Site/Site/Program.cs | 9 +++- src/Site/Site/Site.csproj | 2 + 8 files changed, 113 insertions(+), 39 deletions(-) create mode 100644 src/Site/Site/AI/AibbaKnowledge.cs create mode 100644 src/Site/Site/AI/AibbaNavigationPlugin.cs delete mode 100644 src/Site/Site/AI/AibbaPlugin.cs diff --git a/.editorconfig b/.editorconfig index 2f0f0f2..538effd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,3 +5,6 @@ dotnet_diagnostic.SKEXP0001.severity = none # SKEXP0010: Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. dotnet_diagnostic.SKEXP0010.severity = none + +# SKEXP0050: Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +dotnet_diagnostic.SKEXP0050.severity = none diff --git a/PersonalWebsite.sln b/PersonalWebsite.sln index 5803083..5e03df1 100644 --- a/PersonalWebsite.sln +++ b/PersonalWebsite.sln @@ -9,6 +9,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Site.Client", "src\Site\Sit EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6680D40B-9A58-4838-BCAD-56D382006878}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig .github\workflows\main_erinnmclaughlin.yml = .github\workflows\main_erinnmclaughlin.yml EndProjectSection EndProject diff --git a/src/Site/Site/AI/Aibba.cs b/src/Site/Site/AI/Aibba.cs index 1041da9..948e1eb 100644 --- a/src/Site/Site/AI/Aibba.cs +++ b/src/Site/Site/AI/Aibba.cs @@ -13,10 +13,11 @@ public sealed class Aibba private readonly OpenAIPromptExecutionSettings _promptExecutionSettings = new(); private readonly Queue _queue = []; - public Aibba(IOptions options, AibbaPlugin plugin) + public Aibba(IOptions options, AibbaKnowledge knowledge, AibbaNavigationPlugin navigationPlugin) { _kernel = options.Value.BuildDefaultKernel(); - _kernel.Plugins.AddFromObject(plugin); + navigationPlugin.ApplyToKernel(_kernel); + knowledge.ApplyToKernel(_kernel); _promptExecutionSettings.ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions; @@ -41,7 +42,8 @@ private void InitializeChatHistory() { _messages.AddSystemMessage(""" You are an AI assistant named Aibba and you are running on Erin McLaughlin's personal website. - Erin is the software engineer that programmed you. Talk to users about her. + Erin is the software engineer that programmed you. + Your job is to talk to users about her. Please note that you can recall information about Erin from your memory. """); AddErinMessage("Hi! Welcome to my website. I'm a software engineer with a passion for building context-driven systems."); diff --git a/src/Site/Site/AI/AibbaKnowledge.cs b/src/Site/Site/AI/AibbaKnowledge.cs new file mode 100644 index 0000000..b43d148 --- /dev/null +++ b/src/Site/Site/AI/AibbaKnowledge.cs @@ -0,0 +1,61 @@ +using Microsoft.Extensions.Options; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Connectors.OpenAI; +using Microsoft.SemanticKernel.Memory; +using Microsoft.SemanticKernel.Plugins.Memory; +using System.ComponentModel; + +namespace Site.AI; + +public sealed class AibbaKnowledge +{ + private readonly ISemanticTextMemory _memory; + + public AibbaKnowledge(IOptions options) + { + var memoryBuilder = new MemoryBuilder(); + memoryBuilder.WithOpenAITextEmbeddingGeneration(options.Value.TextEmbeddingModel, options.Value.ApiKey); + memoryBuilder.WithMemoryStore(new VolatileMemoryStore()); + _memory = memoryBuilder.Build(); + } + + internal void ApplyToKernel(Kernel kernel) + { + kernel.Plugins.AddFromObject(this); + kernel.Plugins.AddFromObject(new TextMemoryPlugin(_memory)); + } + + [KernelFunction, Description("Get a link to Erin's GitHub profile.")] + public static string GetGitHubUrl() => "https://github.com/erinnmclaughlin"; + + [KernelFunction, Description("Get a link to Erin's LinkedIn profile.")] + public static string GetLinkedInUrl() => "https://www.linkedin.com/in/e1mclaughlin"; + + [KernelFunction, Description("Get a link to Erin's resume.")] + public static string GetResume() => "https://erinnmclaughlin.github.io/Resume"; + + [KernelFunction, Description("Recall information about Erin.")] + public async Task RecallInformationAsync([Description("The input text to recall information for.")] string question, CancellationToken cancellationToken = default) + { + return await new TextMemoryPlugin(_memory).RecallAsync(question, "AboutErin", cancellationToken: cancellationToken); + } + + internal async Task AddMemoriesAsync() + { + var facts = new string[] + { + "Erin is a full stack software engineer that specializes in C#, .NET, and Blazor.", + "Erin received her Master's degree in Software Engineering from Penn State University in 2023.", + "Erin received her Bachelor's degree in Biology from Bridgewater State University in 2016.", + "Erin is passionate about continuous learning and development.", + "Erin strongly believes that context is key for making technical decisions. She believes there is never a 'one-size-fits-all' solution in software.", + "Erin has mainly focused on building internal web applications for small companies, helping them to increase process efficiency and cross-departmental collaboration.", + "Erin has two cats: Mia and Jax." + }; + + for (int i = 0; i < facts.Length; i++) + { + await _memory.SaveInformationAsync("AboutErin", facts[i], $"info{i+1}"); + } + } +} diff --git a/src/Site/Site/AI/AibbaNavigationPlugin.cs b/src/Site/Site/AI/AibbaNavigationPlugin.cs new file mode 100644 index 0000000..f0b6353 --- /dev/null +++ b/src/Site/Site/AI/AibbaNavigationPlugin.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.SemanticKernel; +using System.ComponentModel; + +namespace Site.AI; + +public sealed class AibbaNavigationPlugin(NavigationManager navigationManager) +{ + private readonly NavigationManager _navigationManager = navigationManager; + + internal void ApplyToKernel(Kernel kernel) + { + kernel.Plugins.AddFromObject(this); + } + + [KernelFunction, Description("Navigate to Erin's GitHub profile.")] + public void NavigateToGitHub() + { + _navigationManager.NavigateTo(AibbaKnowledge.GetGitHubUrl()); + } + + [KernelFunction, Description("Navigate to Erin's LinkedIn profile.")] + public void NavigateToLinkedIn() + { + _navigationManager.NavigateTo(AibbaKnowledge.GetLinkedInUrl()); + } + + [KernelFunction, Description("Navigate to Erin's resume.")] + public void NavigateToResume() + { + _navigationManager.NavigateTo(AibbaKnowledge.GetResume()); + } +} \ No newline at end of file diff --git a/src/Site/Site/AI/AibbaPlugin.cs b/src/Site/Site/AI/AibbaPlugin.cs deleted file mode 100644 index 50420de..0000000 --- a/src/Site/Site/AI/AibbaPlugin.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Microsoft.SemanticKernel; -using System.ComponentModel; - -namespace Site.AI; - -public sealed class AibbaPlugin(NavigationManager navigationManager) -{ - [KernelFunction, Description("Get a link to Erin's GitHub profile.")] - public static string GetGitHubUrl() => "https://github.com/erinnmclaughlin"; - - [KernelFunction, Description("Get a link to Erin's LinkedIn profile.")] - public static string GetLinkedInUrl() => "https://www.linkedin.com/in/e1mclaughlin/"; - - [KernelFunction, Description("Get a link to Erin's resume.")] - public static string GetResume() => "https://erinnmclaughlin.github.io/Resume/"; - - [KernelFunction, Description("Navigate to Erin's GitHub profile.")] - public void NavigateToGitHub() - { - navigationManager.NavigateTo(GetGitHubUrl()); - } - - [KernelFunction, Description("Navigate to Erin's LinkedIn profile.")] - public void NavigateToLinkedIn() - { - navigationManager.NavigateTo(GetLinkedInUrl()); - } - - [KernelFunction, Description("Navigate to Erin's resume.")] - public void NavigateToResume() - { - navigationManager.NavigateTo(GetResume()); - } -} diff --git a/src/Site/Site/Program.cs b/src/Site/Site/Program.cs index 78f1e54..bc4425f 100644 --- a/src/Site/Site/Program.cs +++ b/src/Site/Site/Program.cs @@ -10,7 +10,8 @@ builder.Services.Configure(builder.Configuration.GetSection("AI")); builder.Services.AddScoped(); -builder.Services.AddScoped(); +builder.Services.AddSingleton(); +builder.Services.AddScoped(); builder.Services.AddResponseCompression(o => { @@ -43,4 +44,10 @@ app.MapHub("/aibba"); +using (var scope = app.Services.CreateScope()) +{ + var knowledge = scope.ServiceProvider.GetRequiredService(); + await knowledge.AddMemoriesAsync(); +} + app.Run(); diff --git a/src/Site/Site/Site.csproj b/src/Site/Site/Site.csproj index b211914..7aa71d2 100644 --- a/src/Site/Site/Site.csproj +++ b/src/Site/Site/Site.csproj @@ -10,7 +10,9 @@ + + From 31f0e3bf6d866c1a0e0442d88f3afc2793e0420b Mon Sep 17 00:00:00 2001 From: Erin McLaughlin Date: Thu, 18 Apr 2024 22:00:49 -0400 Subject: [PATCH 5/7] improved memory --- src/Site/Site/AI/AibbaKnowledge.cs | 34 ++++++++++++++++++------------ 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/Site/Site/AI/AibbaKnowledge.cs b/src/Site/Site/AI/AibbaKnowledge.cs index b43d148..50bedeb 100644 --- a/src/Site/Site/AI/AibbaKnowledge.cs +++ b/src/Site/Site/AI/AibbaKnowledge.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.Options; +using Microsoft.KernelMemory; using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.Connectors.OpenAI; -using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.Plugins.Memory; using System.ComponentModel; @@ -9,20 +8,27 @@ namespace Site.AI; public sealed class AibbaKnowledge { - private readonly ISemanticTextMemory _memory; + private readonly MemoryServerless _memory; public AibbaKnowledge(IOptions options) { - var memoryBuilder = new MemoryBuilder(); - memoryBuilder.WithOpenAITextEmbeddingGeneration(options.Value.TextEmbeddingModel, options.Value.ApiKey); - memoryBuilder.WithMemoryStore(new VolatileMemoryStore()); - _memory = memoryBuilder.Build(); + var aiOptions = options.Value; + + _memory = new KernelMemoryBuilder() + .WithOpenAI(new OpenAIConfig + { + TextModel = aiOptions.TextCompletionModel, + TextModelMaxTokenTotal = 16384, + EmbeddingModel = aiOptions.TextEmbeddingModel, + EmbeddingModelMaxTokenTotal = 8191, + APIKey = aiOptions.ApiKey + }) + .Build(); } internal void ApplyToKernel(Kernel kernel) { kernel.Plugins.AddFromObject(this); - kernel.Plugins.AddFromObject(new TextMemoryPlugin(_memory)); } [KernelFunction, Description("Get a link to Erin's GitHub profile.")] @@ -34,10 +40,12 @@ internal void ApplyToKernel(Kernel kernel) [KernelFunction, Description("Get a link to Erin's resume.")] public static string GetResume() => "https://erinnmclaughlin.github.io/Resume"; - [KernelFunction, Description("Recall information about Erin.")] - public async Task RecallInformationAsync([Description("The input text to recall information for.")] string question, CancellationToken cancellationToken = default) + [KernelFunction, Description("Ask a question about Erin.")] + [return: Description("The answer to the question.")] + public async Task Ask([Description("The input text to recall information for.")] string question, CancellationToken cancellationToken = default) { - return await new TextMemoryPlugin(_memory).RecallAsync(question, "AboutErin", cancellationToken: cancellationToken); + var answer = await _memory.AskAsync(question, cancellationToken: cancellationToken); + return answer.NoResult ? "I don't know the answer to that question." : answer.Result; } internal async Task AddMemoriesAsync() @@ -53,9 +61,9 @@ internal async Task AddMemoriesAsync() "Erin has two cats: Mia and Jax." }; - for (int i = 0; i < facts.Length; i++) + foreach (var fact in facts) { - await _memory.SaveInformationAsync("AboutErin", facts[i], $"info{i+1}"); + await _memory.ImportTextAsync(fact); } } } From f58de0e2e2f510453ea0acea738cbde06bf4fb1b Mon Sep 17 00:00:00 2001 From: Erin McLaughlin Date: Thu, 18 Apr 2024 22:08:18 -0400 Subject: [PATCH 6/7] terminal colors --- src/Site/Site.Client/Components/TerminalChat.razor | 2 +- src/Site/Site.Client/Components/TerminalChat.razor.cs | 7 +++++++ src/Site/Site/AI/AibbaKnowledge.cs | 5 +++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Site/Site.Client/Components/TerminalChat.razor b/src/Site/Site.Client/Components/TerminalChat.razor index 6cfe740..6b0a443 100644 --- a/src/Site/Site.Client/Components/TerminalChat.razor +++ b/src/Site/Site.Client/Components/TerminalChat.razor @@ -7,7 +7,7 @@ @foreach (var message in Messages) {
-