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