diff --git a/app/MindWork AI Studio/Components/ChatComponent.razor b/app/MindWork AI Studio/Components/ChatComponent.razor index ccfe1e1..2b8ff2d 100644 --- a/app/MindWork AI Studio/Components/ChatComponent.razor +++ b/app/MindWork AI Studio/Components/ChatComponent.razor @@ -100,6 +100,13 @@ } + @if (this.isStreaming && this.cancellationTokenSource is not null) + { + + + + } + diff --git a/app/MindWork AI Studio/Components/ChatComponent.razor.cs b/app/MindWork AI Studio/Components/ChatComponent.razor.cs index 5017d01..e2159d2 100644 --- a/app/MindWork AI Studio/Components/ChatComponent.razor.cs +++ b/app/MindWork AI Studio/Components/ChatComponent.razor.cs @@ -56,6 +56,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable private bool autoSaveEnabled; private string currentWorkspaceName = string.Empty; private Guid currentWorkspaceId = Guid.Empty; + private CancellationTokenSource? cancellationTokenSource; // Unfortunately, we need the input field reference to blur the focus away. Without // this, we cannot clear the input field. @@ -336,15 +337,20 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable this.scrollRenderCountdown = 2; } - this.StateHasChanged(); - this.Logger.LogDebug($"Start processing user input using provider '{this.Provider.InstanceName}' with model '{this.Provider.Model}'."); - // Use the selected provider to get the AI response. - // By awaiting this line, we wait for the entire - // content to be streamed. - await aiText.CreateFromProviderAsync(this.Provider.CreateProvider(this.Logger), this.SettingsManager, this.Provider.Model, this.ChatThread); + using (this.cancellationTokenSource = new()) + { + this.StateHasChanged(); + + // Use the selected provider to get the AI response. + // By awaiting this line, we wait for the entire + // content to be streamed. + await aiText.CreateFromProviderAsync(this.Provider.CreateProvider(this.Logger), this.SettingsManager, this.Provider.Model, this.ChatThread, this.cancellationTokenSource.Token); + } + this.cancellationTokenSource = null; + // Save the chat: if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY) { @@ -357,6 +363,13 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable this.StateHasChanged(); } + private async Task CancelStreaming() + { + if (this.cancellationTokenSource is not null) + if(!this.cancellationTokenSource.IsCancellationRequested) + await this.cancellationTokenSource.CancelAsync(); + } + private async Task SaveThread() { if(this.ChatThread is null) @@ -679,6 +692,14 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable await this.SaveThread(); this.hasUnsavedChanges = false; } + + if (this.cancellationTokenSource is not null) + { + if(!this.cancellationTokenSource.IsCancellationRequested) + await this.cancellationTokenSource.CancelAsync(); + + this.cancellationTokenSource.Dispose(); + } } #endregion diff --git a/app/MindWork AI Studio/wwwroot/changelog/v0.9.24.md b/app/MindWork AI Studio/wwwroot/changelog/v0.9.24.md index 0c6775c..0c4f18d 100644 --- a/app/MindWork AI Studio/wwwroot/changelog/v0.9.24.md +++ b/app/MindWork AI Studio/wwwroot/changelog/v0.9.24.md @@ -1,4 +1,5 @@ # v0.9.24, build 199 (2025-01-xx xx:xx UTC) - Added a button to remove a message from the chat thread. - Added a button to regenerate the last AI response. -- Added a button to edit the last user message. \ No newline at end of file +- Added a button to edit the last user message. +- Added a button to stop the AI from generating a response. \ No newline at end of file