mirror of
				https://github.com/MindWorkAI/AI-Studio.git
				synced 2025-11-04 05:40:21 +00:00 
			
		
		
		
	Added a button to regenerate the last AI response (#247)
This commit is contained in:
		
							parent
							
								
									5e445f09fa
								
							
						
					
					
						commit
						b2ca49ab92
					
				@ -97,17 +97,45 @@ public sealed record ChatThread
 | 
			
		||||
        logger.LogInformation(logMessage);
 | 
			
		||||
        return systemPromptText;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Removes a content block from this chat thread.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="content">The content block to remove.</param>
 | 
			
		||||
    public void Remove(IContent content)
 | 
			
		||||
    /// <param name="removeForRegenerate">Indicates whether the content block is removed for
 | 
			
		||||
    /// regeneration purposes. True, when the content block is removed for regeneration purposes,
 | 
			
		||||
    /// which will not remove the previous user block if it is hidden from the user.</param>
 | 
			
		||||
    public void Remove(IContent content, bool removeForRegenerate = false)
 | 
			
		||||
    {
 | 
			
		||||
        var block = this.Blocks.FirstOrDefault(x => x.Content == content);
 | 
			
		||||
        if(block is null)
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        //
 | 
			
		||||
        // Remove the previous user block if it is hidden from the user. Otherwise,
 | 
			
		||||
        // the experience might be confusing for the user.
 | 
			
		||||
        //
 | 
			
		||||
        // Explanation, using the ERI assistant as an example:
 | 
			
		||||
        // - The ERI assistant generates for every file a hidden user prompt.
 | 
			
		||||
        // - In the UI, the user can only see the AI's responses, not the hidden user prompts.
 | 
			
		||||
        // - Now, the user removes one AI response
 | 
			
		||||
        // - The hidden user prompt is still there, but the user can't see it.
 | 
			
		||||
        // - Since the user prompt is hidden, neither is it possible to remove nor edit it.
 | 
			
		||||
        // - This method solves this issue by removing the hidden user prompt when the AI response is removed.
 | 
			
		||||
        //
 | 
			
		||||
        if (block.Role is ChatRole.AI && !removeForRegenerate)
 | 
			
		||||
        {
 | 
			
		||||
            var sortedBlocks = this.Blocks.OrderBy(x => x.Time).ToList();
 | 
			
		||||
            var index = sortedBlocks.IndexOf(block);
 | 
			
		||||
            if (index > 0)
 | 
			
		||||
            {
 | 
			
		||||
                var previousBlock = sortedBlocks[index - 1];
 | 
			
		||||
                if (previousBlock.Role is ChatRole.USER && previousBlock.HideFromUser)
 | 
			
		||||
                    this.Blocks.Remove(previousBlock);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Remove the block from the chat thread:
 | 
			
		||||
        this.Blocks.Remove(block);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -12,6 +12,12 @@
 | 
			
		||||
            <MudText Typo="Typo.body1">@this.Role.ToName() (@this.Time)</MudText>
 | 
			
		||||
        </CardHeaderContent>
 | 
			
		||||
        <CardHeaderActions>
 | 
			
		||||
            @if (this.IsLastContentBlock && this.Role is ChatRole.AI && this.RegenerateFunc is not null)
 | 
			
		||||
            {
 | 
			
		||||
                <MudTooltip Text="Regenerate" Placement="Placement.Bottom">
 | 
			
		||||
                    <MudIconButton Icon="@Icons.Material.Filled.Recycling" Color="Color.Tertiary" OnClick="@this.RegenerateBlock"/>
 | 
			
		||||
                </MudTooltip>
 | 
			
		||||
            }
 | 
			
		||||
            @if (this.RemoveBlockFunc is not null)
 | 
			
		||||
            {
 | 
			
		||||
                <MudTooltip Text="Removes this block" Placement="Placement.Bottom">
 | 
			
		||||
 | 
			
		||||
@ -40,10 +40,19 @@ public partial class ContentBlockComponent : ComponentBase
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public string Class { get; set; } = string.Empty;
 | 
			
		||||
    
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public bool IsLastContentBlock { get; set; } = false;
 | 
			
		||||
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public Func<IContent, Task>? RemoveBlockFunc { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public Func<IContent, Task>? RegenerateFunc { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public Func<bool> RegenerateEnabled { get; set; } = () => false;
 | 
			
		||||
    
 | 
			
		||||
    [Inject]
 | 
			
		||||
    private RustService RustService { get; init; } = null!;
 | 
			
		||||
    
 | 
			
		||||
@ -143,4 +152,22 @@ public partial class ContentBlockComponent : ComponentBase
 | 
			
		||||
        if (remove.HasValue && remove.Value)
 | 
			
		||||
            await this.RemoveBlockFunc(this.Content);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    private async Task RegenerateBlock()
 | 
			
		||||
    {
 | 
			
		||||
        if (this.RegenerateFunc is null)
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
        if(this.Role is not ChatRole.AI)
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
        var regenerate = await this.DialogService.ShowMessageBox(
 | 
			
		||||
            "Regenerate Message",
 | 
			
		||||
            "Do you really want to regenerate this message?",
 | 
			
		||||
            "Yes, regenerate it",
 | 
			
		||||
            "No, keep it");
 | 
			
		||||
        
 | 
			
		||||
        if (regenerate.HasValue && regenerate.Value)
 | 
			
		||||
            await this.RegenerateFunc(this.Content);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -7,11 +7,14 @@
 | 
			
		||||
    <ChildContent>
 | 
			
		||||
        @if (this.ChatThread is not null)
 | 
			
		||||
        {
 | 
			
		||||
            @foreach (var block in this.ChatThread.Blocks.OrderBy(n => n.Time))
 | 
			
		||||
            var blocks = this.ChatThread.Blocks.OrderBy(n => n.Time).ToList();
 | 
			
		||||
            for (var i = 0; i < blocks.Count; i++)
 | 
			
		||||
            {
 | 
			
		||||
                var block = blocks[i];
 | 
			
		||||
                var isLastBlock = i == blocks.Count - 1;
 | 
			
		||||
                @if (!block.HideFromUser)
 | 
			
		||||
                {
 | 
			
		||||
                    <ContentBlockComponent Role="@block.Role" Type="@block.ContentType" Time="@block.Time" Content="@block.Content" RemoveBlockFunc="@this.RemoveBlock"/>
 | 
			
		||||
                    <ContentBlockComponent Role="@block.Role" Type="@block.ContentType" Time="@block.Time" Content="@block.Content" RemoveBlockFunc="@this.RemoveBlock" IsLastContentBlock="@isLastBlock" RegenerateFunc="@this.RegenerateBlock" RegenerateEnabled="@(() => this.IsProviderSelected)"/>
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -242,7 +242,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    private async Task SendMessage()
 | 
			
		||||
    private async Task SendMessage(bool reuseLastUserPrompt = false)
 | 
			
		||||
    {
 | 
			
		||||
        if (!this.IsProviderSelected)
 | 
			
		||||
            return;
 | 
			
		||||
@ -252,8 +252,6 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
 | 
			
		||||
        await this.inputField.BlurAsync();
 | 
			
		||||
        
 | 
			
		||||
        // Create a new chat thread if necessary:
 | 
			
		||||
        var threadName = this.ExtractThreadName(this.userInput);
 | 
			
		||||
 | 
			
		||||
        if (this.ChatThread is null)
 | 
			
		||||
        {
 | 
			
		||||
            this.ChatThread = new()
 | 
			
		||||
@ -263,7 +261,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
 | 
			
		||||
                SystemPrompt = SystemPrompts.DEFAULT,
 | 
			
		||||
                WorkspaceId = this.currentWorkspaceId,
 | 
			
		||||
                ChatId = Guid.NewGuid(),
 | 
			
		||||
                Name = threadName,
 | 
			
		||||
                Name = this.ExtractThreadName(this.userInput),
 | 
			
		||||
                Seed = this.RNG.Next(),
 | 
			
		||||
                Blocks = [],
 | 
			
		||||
            };
 | 
			
		||||
@ -274,34 +272,37 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
 | 
			
		||||
        {
 | 
			
		||||
            // Set the thread name if it is empty:
 | 
			
		||||
            if (string.IsNullOrWhiteSpace(this.ChatThread.Name))
 | 
			
		||||
                this.ChatThread.Name = threadName;
 | 
			
		||||
                this.ChatThread.Name = this.ExtractThreadName(this.userInput);
 | 
			
		||||
            
 | 
			
		||||
            // Update provider and profile:
 | 
			
		||||
            this.ChatThread.SelectedProvider = this.Provider.Id;
 | 
			
		||||
            this.ChatThread.SelectedProfile = this.currentProfile.Id;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        //
 | 
			
		||||
        // Add the user message to the thread:
 | 
			
		||||
        //
 | 
			
		||||
        var time = DateTimeOffset.Now;
 | 
			
		||||
        this.ChatThread?.Blocks.Add(new ContentBlock
 | 
			
		||||
        {
 | 
			
		||||
            Time = time,
 | 
			
		||||
            ContentType = ContentType.TEXT,
 | 
			
		||||
            Role = ChatRole.USER,
 | 
			
		||||
            Content = new ContentText
 | 
			
		||||
            {
 | 
			
		||||
                Text = this.userInput,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Save the chat:
 | 
			
		||||
        if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
 | 
			
		||||
        var time = DateTimeOffset.Now;
 | 
			
		||||
        if (!reuseLastUserPrompt)
 | 
			
		||||
        {
 | 
			
		||||
            await this.SaveThread();
 | 
			
		||||
            this.hasUnsavedChanges = false;
 | 
			
		||||
            this.StateHasChanged();
 | 
			
		||||
            //
 | 
			
		||||
            // Add the user message to the thread:
 | 
			
		||||
            //
 | 
			
		||||
            this.ChatThread?.Blocks.Add(new ContentBlock
 | 
			
		||||
            {
 | 
			
		||||
                Time = time,
 | 
			
		||||
                ContentType = ContentType.TEXT,
 | 
			
		||||
                Role = ChatRole.USER,
 | 
			
		||||
                Content = new ContentText
 | 
			
		||||
                {
 | 
			
		||||
                    Text = this.userInput,
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // Save the chat:
 | 
			
		||||
            if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
 | 
			
		||||
            {
 | 
			
		||||
                await this.SaveThread();
 | 
			
		||||
                this.hasUnsavedChanges = false;
 | 
			
		||||
                this.StateHasChanged();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //
 | 
			
		||||
@ -581,6 +582,18 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
 | 
			
		||||
        await this.SaveThread();
 | 
			
		||||
        this.StateHasChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task RegenerateBlock(IContent aiBlock)
 | 
			
		||||
    {
 | 
			
		||||
        if(this.ChatThread is null)
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
        this.ChatThread.Remove(aiBlock, removeForRegenerate: true);
 | 
			
		||||
        this.hasUnsavedChanges = true;
 | 
			
		||||
        this.StateHasChanged();
 | 
			
		||||
        
 | 
			
		||||
        await this.SendMessage(reuseLastUserPrompt: true);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #region Overrides of MSGComponentBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,2 +1,3 @@
 | 
			
		||||
# 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 remove a message from the chat thread.
 | 
			
		||||
- Added a button to regenerate the last AI response.
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user