mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-13 01:33:48 +00:00
Copy entire chats & Rollback chats to certain message (#992)
Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
1 parent
c4ba83f0be
commit
e9ebc02a96
14 files changed
+733
-8
No files matched your search
@@ -350,6 +350,48 @@ public sealed record ChatThread
|
||||
this.Blocks.Remove(block);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rolls this chat thread back, so that the given content becomes the last block of the conversation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Every later block is removed in conversation order, which is the order of the time stamps and
|
||||
/// the order in which the chat shows the blocks. That includes the blocks hidden from the user,
|
||||
/// such as the prompts an assistant sends into a chat: the user can neither see nor remove them,
|
||||
/// so leaving them behind would continue the chat with messages nobody knows about. Hidden blocks
|
||||
/// before the content stay, e.g. the example conversation of a chat template. The managed
|
||||
/// transcripts of the removed blocks are deleted with them.<br/><br/>
|
||||
///
|
||||
/// The augmented data and the AI-selected data sources are reset, too. Both describe the last
|
||||
/// retrieval, not a certain message, so after a rollback nobody knows whether they belong to a
|
||||
/// kept or to a removed one. The next message with active data sources retrieves anew; without
|
||||
/// active data sources, the chat continues without this context. The data source options stay,
|
||||
/// because they are the user's choice rather than the result of a message.<br/><br/>
|
||||
///
|
||||
/// What stays as well is everything the thread ratchets for security reasons, namely the data
|
||||
/// security and the required provider confidence. Both only ever tighten, because the data which
|
||||
/// raised them was seen by this thread. Removing the message that brought it in does not unsee
|
||||
/// it, so the chat keeps demanding the same of every provider which continues it.
|
||||
/// </remarks>
|
||||
/// <param name="content">The content to keep as the last block.</param>
|
||||
/// <returns>True when one or more later blocks were removed. False when the content is unknown or already the last block; the thread stays unchanged then.</returns>
|
||||
public bool RollBackTo(IContent content)
|
||||
{
|
||||
var sortedBlocks = this.Blocks.OrderBy(x => x.Time).ToList();
|
||||
var blockIndex = sortedBlocks.FindIndex(block => ReferenceEquals(block.Content, content));
|
||||
if (blockIndex < 0 || blockIndex == sortedBlocks.Count - 1)
|
||||
return false;
|
||||
|
||||
foreach (var block in sortedBlocks.Skip(blockIndex + 1))
|
||||
{
|
||||
DeleteManagedAttachments(block);
|
||||
this.Blocks.Remove(block);
|
||||
}
|
||||
|
||||
this.AugmentedData = string.Empty;
|
||||
this.AISelectedDataSources = [];
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void DeleteManagedAttachments(ContentBlock block)
|
||||
{
|
||||
if (block.Content is not ContentText textContent)
|
||||
|
||||
@@ -70,6 +70,12 @@
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Recycling" Color="Color.Default" Disabled="@(!this.RegenerateEnabled())" OnClick="@this.RegenerateBlock"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
@if (!this.IsLastContentBlock && this.Role is ChatRole.AI && this.RollbackFunc is not null)
|
||||
{
|
||||
<MudTooltip Text="@T("Roll back to this response")" Placement="Placement.Bottom">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Restore" Color="Color.Default" Disabled="@(!this.RollbackEnabled())" OnClick="@this.RollbackBlock"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
@if (this.RemoveBlockFunc is not null)
|
||||
{
|
||||
<MudTooltip Text="@T("Removes this block")" Placement="Placement.Bottom">
|
||||
|
||||
@@ -76,6 +76,9 @@ public partial class ContentBlockComponent : MSGComponentBase
|
||||
|
||||
[Parameter]
|
||||
public Func<IContent, Task>? RegenerateFunc { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<IContent, Task>? RollbackFunc { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<IContent, Task>? EditLastBlockFunc { get; set; }
|
||||
@@ -86,6 +89,9 @@ public partial class ContentBlockComponent : MSGComponentBase
|
||||
[Parameter]
|
||||
public Func<bool> RegenerateEnabled { get; set; } = () => false;
|
||||
|
||||
[Parameter]
|
||||
public Func<bool> RollbackEnabled { get; set; } = () => false;
|
||||
|
||||
/// <summary>
|
||||
/// What the export offers, used both as the label of the export button and as the title of
|
||||
/// the save dialog.
|
||||
@@ -780,6 +786,21 @@ public partial class ContentBlockComponent : MSGComponentBase
|
||||
if (regenerate.HasValue && regenerate.Value)
|
||||
await this.RegenerateFunc(this.Content);
|
||||
}
|
||||
|
||||
private async Task RollbackBlock()
|
||||
{
|
||||
if (this.RollbackFunc is null || this.Role is not ChatRole.AI || !this.RollbackEnabled())
|
||||
return;
|
||||
|
||||
var rollback = await this.DialogService.ShowMessageBox(
|
||||
T("Roll Back Chat"),
|
||||
T("Do you really want to roll back this chat to this AI response? All later messages and their attachments will be permanently removed."),
|
||||
T("Yes, roll back the chat"),
|
||||
T("No, keep it"));
|
||||
|
||||
if (rollback.HasValue && rollback.Value)
|
||||
await this.RollbackFunc(this.Content);
|
||||
}
|
||||
|
||||
private async Task EditLastBlock()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user