Implemented local directory dialog

This commit is contained in:
Thorsten Sommer 2025-01-08 20:21:21 +01:00
parent 03c9257580
commit 0c5417ad31
Signed by: tsommer
GPG Key ID: 371BBA77A02C0108
2 changed files with 211 additions and 0 deletions

View File

@ -0,0 +1,79 @@
<MudDialog>
<DialogContent>
<MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues">
@* ReSharper disable once CSharpWarnings::CS8974 *@
<MudTextField
T="string"
@bind-Text="@this.dataName"
Label="Data Source Name"
Class="mb-6"
MaxLength="40"
Counter="40"
Immediate="@true"
Validation="@this.ValidateName"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Lightbulb"
AdornmentColor="Color.Info"
UserAttributes="@SPELLCHECK_ATTRIBUTES"
/>
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
Select a root directory for this data source. All data in this directory and all
its subdirectories will be processed for this data source.
</MudJustifiedText>
<SelectDirectory @bind-Directory="@this.dataPath" Label="Selected base directory for this data source" DirectoryDialogTitle="Select the base directory" Validation="@this.ValidatePath" />
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
In order for the AI to be able to determine the appropriate data at any time, you must
choose an embedding method.
</MudJustifiedText>
<MudSelect @bind-Value="@this.dataEmbeddingId" Label="Embedding" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.ValidateEmbeddingId">
@foreach (var embedding in this.AvailableEmbeddings)
{
<MudSelectItem Value="@embedding.Value">@embedding.Name</MudSelectItem>
}
</MudSelect>
@if (!string.IsNullOrWhiteSpace(this.dataEmbeddingId))
{
if (this.SelectedCloudEmbedding)
{
<MudJustifiedText Typo="Typo.body1" Color="Color.Error" Class="mb-3">
@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.
}
</MudJustifiedText>
<MudTextSwitch @bind-Value="@this.dataUserAcknowledgedCloudEmbedding" Label="I confirm that I have read and understood the above" LabelOn="Yes, please send my data to the cloud" LabelOff="No, I will chose another embedding" Validation="@this.ValidateUserAcknowledgedCloudEmbedding"/>
}
else
{
<MudJustifiedText Typo="Typo.body1" Color="Color.Tertiary" Class="mb-3">
The embedding you selected runs locally or in your organization. Your data is not sent to the cloud.
</MudJustifiedText>
}
}
</MudForm>
<Issues IssuesData="@this.dataIssues"/>
</DialogContent>
<DialogActions>
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">Cancel</MudButton>
<MudButton OnClick="@this.Store" Variant="Variant.Filled" Color="Color.Primary">
@if(this.IsEditing)
{
@:Update
}
else
{
@:Add
}
</MudButton>
</DialogActions>
</MudDialog>

View File

@ -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<ConfigurationSelectData<string>> AvailableEmbeddings { get; set; } = [];
[Inject]
private SettingsManager SettingsManager { get; init; } = null!;
private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
/// <summary>
/// The list of used data source names. We need this to check for uniqueness.
/// </summary>
private List<string> 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();
}