Implemented feature to start new chats from a workspace

This commit is contained in:
Thorsten Sommer 2024-07-12 19:30:06 +02:00
parent ce8f50ee66
commit 733b2b1ac9
Signed by: tsommer
GPG Key ID: 371BBA77A02C0108
4 changed files with 80 additions and 13 deletions

View File

@ -14,7 +14,16 @@
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item" CanExpand="@treeItem.Expandable" Items="@treeItem.Children" OnClick="() => this.LoadChat(treeItem.Path, true)">
<BodyContent>
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
<MudText Style="justify-self: start;">@treeItem.Text</MudText>
<MudText Style="justify-self: start;">
@if (string.IsNullOrWhiteSpace(treeItem.Text))
{
@("Empty chat")
}
else
{
@treeItem.Text
}
</MudText>
<div style="justify-self: end;">
<MudTooltip Text="Move to workspace" Placement="@WORKSPACE_ITEM_TOOLTIP_PLACEMENT">

View File

@ -20,12 +20,18 @@ public partial class Workspaces : ComponentBase
[Inject]
private IDialogService DialogService { get; set; } = null!;
[Inject]
public Random RNG { get; set; } = null!;
[Parameter]
public ChatThread? CurrentChatThread { get; set; }
[Parameter]
public EventCallback<ChatThread> CurrentChatThreadChanged { get; set; }
[Parameter]
public Func<Task> LoadedChatWasChanged { get; set; } = () => Task.CompletedTask;
private const Placement WORKSPACE_ITEM_TOOLTIP_PLACEMENT = Placement.Bottom;
private static readonly JsonSerializerOptions JSON_OPTIONS = new()
@ -123,7 +129,7 @@ public partial class Workspaces : ComponentBase
return tempChildren;
}
private async Task<string> LoadWorkspaceName(Guid workspaceId)
public async Task<string> LoadWorkspaceName(Guid workspaceId)
{
if(workspaceId == Guid.Empty)
return string.Empty;
@ -238,6 +244,7 @@ public partial class Workspaces : ComponentBase
{
this.CurrentChatThread = chat;
await this.CurrentChatThreadChanged.InvokeAsync(this.CurrentChatThread);
await this.LoadedChatWasChanged();
}
return chat;
@ -289,6 +296,7 @@ public partial class Workspaces : ComponentBase
{
this.CurrentChatThread = null;
await this.CurrentChatThreadChanged.InvokeAsync(this.CurrentChatThread);
await this.LoadedChatWasChanged();
}
}
@ -429,11 +437,12 @@ public partial class Workspaces : ComponentBase
// Update the chat's workspace:
chat.WorkspaceId = workspaceId;
// Handle the case, where the chat is the active chat:
// Handle the case where the chat is the active chat:
if (this.CurrentChatThread?.ChatId == chat.ChatId)
{
this.CurrentChatThread = chat;
await this.CurrentChatThreadChanged.InvokeAsync(this.CurrentChatThread);
await this.LoadedChatWasChanged();
}
await this.StoreChat(chat);
@ -441,6 +450,21 @@ public partial class Workspaces : ComponentBase
private async Task AddChat(string workspacePath)
{
var workspaceId = Guid.Parse(Path.GetFileName(workspacePath));
var chat = new ChatThread
{
WorkspaceId = workspaceId,
ChatId = Guid.NewGuid(),
Name = string.Empty,
Seed = this.RNG.Next(),
SystemPrompt = "You are a helpful assistant!",
Blocks = [],
};
var chatPath = Path.Join(workspacePath, chat.ChatId.ToString());
await this.StoreChat(chat);
await this.LoadChat(chatPath, switchToChat: true);
await this.LoadTreeItems();
}
}

View File

@ -3,7 +3,14 @@
@using AIStudio.Settings
<MudText Typo="Typo.h3" Class="mb-2 mr-3">
Chats
@if (this.chatThread is not null && this.chatThread.WorkspaceId != Guid.Empty)
{
@($"Chat in Workspace \"{this.currentWorkspaceName}\"")
}
else
{
@("Temporary Chat")
}
</MudText>
<MudSelect T="Provider" @bind-Value="@this.selectedProvider" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Apps" Margin="Margin.Dense" Label="Provider" Class="mb-2 rounded-lg" Variant="Variant.Outlined">
@ -67,7 +74,7 @@
</MudStack>
</MudDrawerHeader>
<MudDrawerContainer Class="ml-6">
<Workspaces @ref="this.workspaces" @bind-CurrentChatThread="@this.chatThread"/>
<Workspaces @ref="this.workspaces" @bind-CurrentChatThread="@this.chatThread" LoadedChatWasChanged="this.LoadedChatChanged"/>
</MudDrawerContainer>
</MudDrawer>
}

View File

@ -36,6 +36,7 @@ public partial class Chat : ComponentBase, IAsyncDisposable
private bool hasUnsavedChanges;
private bool isStreaming;
private string userInput = string.Empty;
private string currentWorkspaceName = string.Empty;
private bool workspacesVisible;
private Workspaces? workspaces;
@ -71,15 +72,25 @@ public partial class Chat : ComponentBase, IAsyncDisposable
// Create a new chat thread if necessary:
var threadName = this.ExtractThreadName(this.userInput);
this.chatThread ??= new()
if (this.chatThread is null)
{
WorkspaceId = Guid.Empty,
ChatId = Guid.NewGuid(),
Name = threadName,
Seed = this.RNG.Next(),
SystemPrompt = "You are a helpful assistant!",
Blocks = [],
};
this.chatThread = new()
{
WorkspaceId = Guid.Empty,
ChatId = Guid.NewGuid(),
Name = threadName,
Seed = this.RNG.Next(),
SystemPrompt = "You are a helpful assistant!",
Blocks = [],
};
}
else
{
// Set the thread name if it is empty:
if (string.IsNullOrWhiteSpace(this.chatThread.Name))
this.chatThread.Name = threadName;
}
//
// Add the user message to the thread:
@ -226,6 +237,7 @@ public partial class Chat : ComponentBase, IAsyncDisposable
this.isStreaming = false;
this.hasUnsavedChanges = false;
this.userInput = string.Empty;
this.currentWorkspaceName = string.Empty;
await this.inputField.Clear();
}
@ -267,6 +279,21 @@ public partial class Chat : ComponentBase, IAsyncDisposable
this.chatThread!.WorkspaceId = workspaceId;
await this.SaveThread();
this.currentWorkspaceName = await this.workspaces.LoadWorkspaceName(this.chatThread.WorkspaceId);
}
private async Task LoadedChatChanged()
{
if(this.workspaces is null)
return;
this.isStreaming = false;
this.hasUnsavedChanges = false;
this.userInput = string.Empty;
this.currentWorkspaceName = this.chatThread is null ? string.Empty : await this.workspaces.LoadWorkspaceName(this.chatThread.WorkspaceId);
await this.inputField.Clear();
}
#region Implementation of IAsyncDisposable