AI-Studio/app/MindWork AI Studio/Components/Blocks/Workspaces.razor.cs

236 lines
7.9 KiB
C#
Raw Normal View History

using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using AIStudio.Chat;
using AIStudio.Settings;
using Microsoft.AspNetCore.Components;
2024-07-04 09:00:13 +00:00
namespace AIStudio.Components.Blocks;
public partial class Workspaces : ComponentBase
{
[Inject]
private SettingsManager SettingsManager { get; set; } = null!;
[Parameter]
public ChatThread? CurrentChatThread { get; set; }
[Parameter]
public EventCallback<ChatThread> CurrentChatThreadChanged { get; set; }
private static readonly JsonSerializerOptions JSON_OPTIONS = new()
{
WriteIndented = true,
AllowTrailingCommas = true,
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
Converters =
{
new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseUpper),
}
};
2024-07-11 06:54:03 +00:00
private readonly HashSet<ITreeItem> treeItems = new();
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
{
//
// Notice: In order to get the server-based loading to work, we need to respect the following rules:
// - We must have initial tree items
// - Those initial tree items cannot have children
// - When assigning the tree items to the MudTreeViewItem component, we must set the Value property to the value of the item
//
await this.LoadTreeItems();
await base.OnInitializedAsync();
}
#endregion
private async Task LoadTreeItems()
{
this.treeItems.Clear();
2024-07-11 06:54:03 +00:00
this.treeItems.Add(new TreeItemData
{
Depth = 0,
Branch = WorkspaceBranch.WORKSPACES,
Text = "Workspaces",
Icon = Icons.Material.Filled.Folder,
Expandable = true,
2024-07-11 06:54:03 +00:00
Path = "root",
Children = await this.LoadWorkspaces(),
});
2024-07-11 06:54:03 +00:00
this.treeItems.Add(new TreeDivider());
this.treeItems.Add(new TreeItemData
{
Depth = 0,
Branch = WorkspaceBranch.TEMPORARY_CHATS,
Text = "Temporary chats",
Icon = Icons.Material.Filled.Timer,
Expandable = true,
2024-07-11 06:54:03 +00:00
Path = "temp",
Children = await this.LoadTemporaryChats(),
});
}
2024-07-11 06:54:03 +00:00
private async Task<HashSet<ITreeItem>> LoadTemporaryChats()
{
2024-07-11 06:54:03 +00:00
var tempChildren = new HashSet<ITreeItem>();
//
// Search for workspace folders in the data directory:
//
// Get the workspace root directory:
var temporaryDirectories = Path.Join(SettingsManager.DataDirectory, "tempChats");
// Ensure the directory exists:
Directory.CreateDirectory(temporaryDirectories);
// Enumerate the workspace directories:
foreach (var tempChatDirPath in Directory.EnumerateDirectories(temporaryDirectories))
{
// Read the `name` file:
var chatNamePath = Path.Join(tempChatDirPath, "name");
var chatName = await File.ReadAllTextAsync(chatNamePath, Encoding.UTF8);
2024-07-11 06:54:03 +00:00
tempChildren.Add(new TreeItemData
{
IsChat = true,
Depth = 1,
Branch = WorkspaceBranch.TEMPORARY_CHATS,
Text = chatName,
Icon = Icons.Material.Filled.Timer,
Expandable = false,
2024-07-11 06:54:03 +00:00
Path = tempChatDirPath,
});
}
return tempChildren;
}
private async Task<HashSet<ITreeItem<string>>> LoadWorkspaces()
{
2024-07-11 06:54:03 +00:00
var workspaces = new HashSet<ITreeItem>();
//
// Search for workspace folders in the data directory:
//
// Get the workspace root directory:
var workspaceDirectories = Path.Join(SettingsManager.DataDirectory, "workspaces");
// Ensure the directory exists:
Directory.CreateDirectory(workspaceDirectories);
// Enumerate the workspace directories:
foreach (var workspaceDirPath in Directory.EnumerateDirectories(workspaceDirectories))
{
// Read the `name` file:
var workspaceNamePath = Path.Join(workspaceDirPath, "name");
var workspaceName = await File.ReadAllTextAsync(workspaceNamePath, Encoding.UTF8);
2024-07-11 06:54:03 +00:00
workspaces.Add(new TreeItemData
{
IsChat = false,
Depth = 1,
Branch = WorkspaceBranch.WORKSPACES,
Text = workspaceName,
Icon = Icons.Material.Filled.Description,
Expandable = true,
2024-07-11 06:54:03 +00:00
Path = workspaceDirPath,
Children = await this.LoadWorkspaceChats(workspaceDirPath),
});
}
2024-07-11 06:54:03 +00:00
workspaces.Add(new TreeButton(WorkspaceBranch.WORKSPACES, 1, "Add workspace",Icons.Material.Filled.Add));
return workspaces;
}
2024-07-11 06:54:03 +00:00
private async Task<HashSet<ITreeItem>> LoadWorkspaceChats(string workspacePath)
{
2024-07-11 06:54:03 +00:00
var workspaceChats = new HashSet<ITreeItem>();
// Enumerate the workspace directory:
foreach (var chatPath in Directory.EnumerateDirectories(workspacePath))
{
// Read the `name` file:
var chatNamePath = Path.Join(chatPath, "name");
var chatName = await File.ReadAllTextAsync(chatNamePath, Encoding.UTF8);
2024-07-11 06:54:03 +00:00
workspaceChats.Add(new TreeItemData
{
IsChat = true,
Depth = 2,
Branch = WorkspaceBranch.WORKSPACES,
Text = chatName,
Icon = Icons.Material.Filled.Chat,
Expandable = false,
2024-07-11 06:54:03 +00:00
Path = chatPath,
});
}
2024-07-11 06:54:03 +00:00
workspaceChats.Add(new TreeButton(WorkspaceBranch.WORKSPACES, 2, "Add chat",Icons.Material.Filled.Add));
return workspaceChats;
}
2024-07-11 06:54:03 +00:00
public async Task StoreChat(ChatThread chat)
{
string chatDirectory;
2024-07-11 06:54:03 +00:00
if (chat.WorkspaceId == Guid.Empty)
chatDirectory = Path.Join(SettingsManager.DataDirectory, "tempChats", chat.ChatId.ToString());
else
2024-07-11 06:54:03 +00:00
chatDirectory = Path.Join(SettingsManager.DataDirectory, "workspaces", chat.WorkspaceId.ToString(), chat.ChatId.ToString());
// Ensure the directory exists:
Directory.CreateDirectory(chatDirectory);
// Save the chat name:
var chatNamePath = Path.Join(chatDirectory, "name");
2024-07-11 06:54:03 +00:00
await File.WriteAllTextAsync(chatNamePath, chat.Name);
// Save the thread as thread.json:
var chatPath = Path.Join(chatDirectory, "thread.json");
2024-07-11 06:54:03 +00:00
await File.WriteAllTextAsync(chatPath, JsonSerializer.Serialize(chat, JSON_OPTIONS), Encoding.UTF8);
// Reload the tree items:
await this.LoadTreeItems();
this.StateHasChanged();
}
private async Task LoadChat(string? chatPath)
{
if(string.IsNullOrWhiteSpace(chatPath))
{
Console.WriteLine("Error: chat path is empty.");
return;
}
if(!Directory.Exists(chatPath))
{
Console.WriteLine($"Error: chat not found: '{chatPath}'");
return;
}
try
{
var chatData = await File.ReadAllTextAsync(Path.Join(chatPath, "thread.json"), Encoding.UTF8);
this.CurrentChatThread = JsonSerializer.Deserialize<ChatThread>(chatData, JSON_OPTIONS);
await this.CurrentChatThreadChanged.InvokeAsync(this.CurrentChatThread);
Console.WriteLine($"Loaded chat: {this.CurrentChatThread?.Name}");
}
catch (Exception e)
{
Console.WriteLine(e);
Console.WriteLine(e.StackTrace);
throw;
}
}
2024-07-04 09:00:13 +00:00
}