From 0c5417ad31a137f247b602fc91f0c877e680f0c2 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Wed, 8 Jan 2025 20:21:21 +0100 Subject: [PATCH] Implemented local directory dialog --- .../DataSourceLocalDirectoryDialog.razor | 79 +++++++++++ .../DataSourceLocalDirectoryDialog.razor.cs | 132 ++++++++++++++++++ 2 files changed, 211 insertions(+) diff --git a/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor b/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor index e69de29b..327350bb 100644 --- a/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor +++ b/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor @@ -0,0 +1,79 @@ + + + + @* ReSharper disable once CSharpWarnings::CS8974 *@ + + + + Select a root directory for this data source. All data in this directory and all + its subdirectories will be processed for this data source. + + + + + In order for the AI to be able to determine the appropriate data at any time, you must + choose an embedding method. + + + @foreach (var embedding in this.AvailableEmbeddings) + { + @embedding.Name + } + + + @if (!string.IsNullOrWhiteSpace(this.dataEmbeddingId)) + { + if (this.SelectedCloudEmbedding) + { + + @if (string.IsNullOrWhiteSpace(this.dataPath)) + { + @: Please note: the embedding you selected runs in the cloud. All your data will be sent to the cloud. + @: Please confirm that you have read and understood this. + } + else + { + @: Please note: the embedding you selected runs in the cloud. All your data from the + @: folder '@this.dataPath' and all its subdirectories will be sent to the cloud. Please + @: confirm that you have read and understood this. + } + + + } + else + { + + The embedding you selected runs locally or in your organization. Your data is not sent to the cloud. + + } + } + + + + + Cancel + + @if(this.IsEditing) + { + @:Update + } + else + { + @:Add + } + + + \ No newline at end of file diff --git a/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor.cs b/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor.cs index b45c70ab..2210e908 100644 --- a/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor.cs +++ b/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor.cs @@ -1,3 +1,4 @@ +using AIStudio.Settings; using AIStudio.Settings.DataModel; using Microsoft.AspNetCore.Components; @@ -6,9 +7,140 @@ namespace AIStudio.Dialogs; public partial class DataSourceLocalDirectoryDialog : ComponentBase { + [CascadingParameter] + private MudDialogInstance MudDialog { get; set; } = null!; + [Parameter] public bool IsEditing { get; set; } [Parameter] public DataSourceLocalDirectory DataSource { get; set; } + + [Parameter] + public IReadOnlyList> AvailableEmbeddings { get; set; } = []; + + [Inject] + private SettingsManager SettingsManager { get; init; } = null!; + + private static readonly Dictionary SPELLCHECK_ATTRIBUTES = new(); + + /// + /// The list of used data source names. We need this to check for uniqueness. + /// + private List UsedDataSourcesNames { get; set; } = []; + + private bool dataIsValid; + private string[] dataIssues = []; + private string dataEditingPreviousInstanceName = string.Empty; + + private uint dataNum; + private string dataId = Guid.NewGuid().ToString(); + private string dataName = string.Empty; + private bool dataUserAcknowledgedCloudEmbedding; + private string dataEmbeddingId = string.Empty; + private string dataPath = string.Empty; + + // We get the form reference from Blazor code to validate it manually: + private MudForm form = null!; + + #region Overrides of ComponentBase + + protected override async Task OnInitializedAsync() + { + // Configure the spellchecking for the instance name input: + this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES); + + // Load the used instance names: + this.UsedDataSourcesNames = this.SettingsManager.ConfigurationData.DataSources.Select(x => x.Name.ToLowerInvariant()).ToList(); + + // When editing, we need to load the data: + if(this.IsEditing) + { + this.dataEditingPreviousInstanceName = this.DataSource.Name.ToLowerInvariant(); + this.dataNum = this.DataSource.Num; + this.dataId = this.DataSource.Id; + this.dataName = this.DataSource.Name; + this.dataEmbeddingId = this.DataSource.EmbeddingId; + this.dataPath = this.DataSource.Path; + } + + await base.OnInitializedAsync(); + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + // Reset the validation when not editing and on the first render. + // We don't want to show validation errors when the user opens the dialog. + if(!this.IsEditing && firstRender) + this.form.ResetValidation(); + + await base.OnAfterRenderAsync(firstRender); + } + + #endregion + + private string? ValidateName(string value) + { + if(string.IsNullOrWhiteSpace(value)) + return "The name must not be empty."; + + if (value.Length > 40) + return "The name must not exceed 40 characters."; + + var lowerName = value.ToLowerInvariant(); + if(lowerName != this.dataEditingPreviousInstanceName && this.UsedDataSourcesNames.Contains(lowerName)) + return "The name is already used by another data source. Please choose a different name."; + + return null; + } + + private string? ValidatePath(string value) + { + if(string.IsNullOrWhiteSpace(value)) + return "The path must not be empty. Please select a directory."; + + return null; + } + + private string? ValidateEmbeddingId(string value) + { + if(string.IsNullOrWhiteSpace(value)) + return "Please select an embedding provider."; + + return null; + } + + private string? ValidateUserAcknowledgedCloudEmbedding(bool value) + { + if(this.SelectedCloudEmbedding && !value) + return "Please acknowledge that you are aware of the cloud embedding implications."; + + return null; + } + + private bool SelectedCloudEmbedding => !this.SettingsManager.ConfigurationData.EmbeddingProviders.FirstOrDefault(x => x.Id == this.dataEmbeddingId).IsSelfHosted; + + private DataSourceLocalDirectory CreateDataSource() => new() + { + Id = this.dataId, + Num = this.dataNum, + Name = this.dataName, + Type = DataSourceType.LOCAL_DIRECTORY, + EmbeddingId = this.dataEmbeddingId, + Path = this.dataPath, + }; + + private async Task Store() + { + await this.form.Validate(); + + // When the data is not valid, we don't store it: + if (!this.dataIsValid) + return; + + var addedDataSource = this.CreateDataSource(); + this.MudDialog.Close(DialogResult.Ok(addedDataSource)); + } + + private void Cancel() => this.MudDialog.Cancel(); } \ No newline at end of file