diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelDataSources.razor b/app/MindWork AI Studio/Components/Settings/SettingsPanelDataSources.razor new file mode 100644 index 00000000..9d5be6f2 --- /dev/null +++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelDataSources.razor @@ -0,0 +1,65 @@ +@using AIStudio.Settings +@using AIStudio.Settings.DataModel +@inherits SettingsPanelBase + +@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager)) +{ + + + + Configured Data Sources + + + TODO + + + + + + + + + + + + # + Name + Type + Embedding + Actions + + + @context.Num + @context.Name + @context.Type.GetDisplayName() + @this.GetEmbeddingName(context) + + + @if (context is IERIDataSource) + { + + Show Information + + } + + Edit + + + Delete + + + + + + @if (this.SettingsManager.ConfigurationData.DataSources.Count == 0) + { + No data sources configured yet. + } + + + External Data (ERI-Server v1) + Local Directory + Local File + + +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelDataSources.razor.cs b/app/MindWork AI Studio/Components/Settings/SettingsPanelDataSources.razor.cs new file mode 100644 index 00000000..528ea2e7 --- /dev/null +++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelDataSources.razor.cs @@ -0,0 +1,216 @@ +using AIStudio.Dialogs; +using AIStudio.Settings; +using AIStudio.Settings.DataModel; + +using ERI_Client.V1; + +using Microsoft.AspNetCore.Components; + +using DialogOptions = AIStudio.Dialogs.DialogOptions; + +namespace AIStudio.Components.Settings; + +public partial class SettingsPanelDataSources : SettingsPanelBase +{ + [Parameter] + public List> AvailableDataSources { get; set; } = new(); + + [Parameter] + public EventCallback>> AvailableDataSourcesChanged { get; set; } + + private string GetEmbeddingName(IDataSource dataSource) + { + if(dataSource is IInternalDataSource internalDataSource) + { + var matchedEmbedding = this.SettingsManager.ConfigurationData.EmbeddingProviders.FirstOrDefault(x => x.Id == internalDataSource.EmbeddingId); + if(matchedEmbedding == default) + return "No valid embedding"; + + return matchedEmbedding.Name; + } + + if(dataSource is IExternalDataSource) + return "External (ERI)"; + + return "Unknown"; + } + + private async Task AddDataSource(DataSourceType type) + { + IDataSource? addedDataSource = null; + switch (type) + { + case DataSourceType.LOCAL_FILE: + var localFileDialogParameters = new DialogParameters + { + { x => x.IsEditing, false }, + }; + + var localFileDialogReference = await this.DialogService.ShowAsync("Add Local File as Data Source", localFileDialogParameters, DialogOptions.FULLSCREEN); + var localFileDialogResult = await localFileDialogReference.Result; + if (localFileDialogResult is null || localFileDialogResult.Canceled) + return; + + var localFile = (DataSourceLocalFile)localFileDialogResult.Data!; + localFile = localFile with { Num = this.SettingsManager.ConfigurationData.NextDataSourceNum++ }; + addedDataSource = localFile; + break; + + case DataSourceType.LOCAL_DIRECTORY: + var localDirectoryDialogParameters = new DialogParameters + { + { x => x.IsEditing, false }, + }; + + var localDirectoryDialogReference = await this.DialogService.ShowAsync("Add Local Directory as Data Source", localDirectoryDialogParameters, DialogOptions.FULLSCREEN); + var localDirectoryDialogResult = await localDirectoryDialogReference.Result; + if (localDirectoryDialogResult is null || localDirectoryDialogResult.Canceled) + return; + + var localDirectory = (DataSourceLocalDirectory)localDirectoryDialogResult.Data!; + localDirectory = localDirectory with { Num = this.SettingsManager.ConfigurationData.NextDataSourceNum++ }; + addedDataSource = localDirectory; + break; + + case DataSourceType.ERI_V1: + var eriDialogParameters = new DialogParameters + { + { x => x.IsEditing, false }, + }; + + var eriDialogReference = await this.DialogService.ShowAsync("Add ERI v1 Data Source", eriDialogParameters, DialogOptions.FULLSCREEN); + var eriDialogResult = await eriDialogReference.Result; + if (eriDialogResult is null || eriDialogResult.Canceled) + return; + + var eriDataSource = (DataSourceERI_V1)eriDialogResult.Data!; + eriDataSource = eriDataSource with { Num = this.SettingsManager.ConfigurationData.NextDataSourceNum++ }; + addedDataSource = eriDataSource; + break; + } + + if(addedDataSource is null) + return; + + this.SettingsManager.ConfigurationData.DataSources.Add(addedDataSource); + await this.UpdateDataSources(); + await this.SettingsManager.StoreSettings(); + await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED); + } + + private async Task EditDataSource(IDataSource dataSource) + { + IDataSource? editedDataSource = null; + switch (dataSource) + { + case DataSourceLocalFile localFile: + var localFileDialogParameters = new DialogParameters + { + { x => x.IsEditing, false }, + { x => x.DataSource, localFile }, + }; + + var localFileDialogReference = await this.DialogService.ShowAsync("Edit Local File Data Source", localFileDialogParameters, DialogOptions.FULLSCREEN); + var localFileDialogResult = await localFileDialogReference.Result; + if (localFileDialogResult is null || localFileDialogResult.Canceled) + return; + + editedDataSource = (DataSourceLocalFile)localFileDialogResult.Data!; + break; + + case DataSourceLocalDirectory localDirectory: + var localDirectoryDialogParameters = new DialogParameters + { + { x => x.IsEditing, false }, + { x => x.DataSource, localDirectory }, + }; + + var localDirectoryDialogReference = await this.DialogService.ShowAsync("Edit Local Directory Data Source", localDirectoryDialogParameters, DialogOptions.FULLSCREEN); + var localDirectoryDialogResult = await localDirectoryDialogReference.Result; + if (localDirectoryDialogResult is null || localDirectoryDialogResult.Canceled) + return; + + editedDataSource = (DataSourceLocalDirectory)localDirectoryDialogResult.Data!; + break; + + case DataSourceERI_V1 eriDataSource: + var eriDialogParameters = new DialogParameters + { + { x => x.IsEditing, false }, + { x => x.DataSource, eriDataSource }, + }; + + var eriDialogReference = await this.DialogService.ShowAsync("Edit ERI v1 Data Source", eriDialogParameters, DialogOptions.FULLSCREEN); + var eriDialogResult = await eriDialogReference.Result; + if (eriDialogResult is null || eriDialogResult.Canceled) + return; + + editedDataSource = (DataSourceERI_V1)eriDialogResult.Data!; + break; + } + + if(editedDataSource is null) + return; + + this.SettingsManager.ConfigurationData.DataSources[this.SettingsManager.ConfigurationData.DataSources.IndexOf(dataSource)] = editedDataSource; + + await this.UpdateDataSources(); + await this.SettingsManager.StoreSettings(); + await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED); + } + + private async Task DeleteDataSource(IDataSource dataSource) + { + var dialogParameters = new DialogParameters + { + { "Message", $"Are you sure you want to delete the data source '{dataSource.Name}' of type {dataSource.Type.GetDisplayName()}?" }, + }; + + var dialogReference = await this.DialogService.ShowAsync("Delete Data Source", dialogParameters, DialogOptions.FULLSCREEN); + var dialogResult = await dialogReference.Result; + if (dialogResult is null || dialogResult.Canceled) + return; + + var applyChanges = dataSource is IInternalDataSource; + + // External data sources may need a secret for authentication: + if (dataSource is IExternalDataSource externalDataSource) + { + // When the auth method is NONE or KERBEROS, we don't need to delete a secret. + // In the case of KERBEROS, we don't store the Kerberos ticket in the secret store. + if(dataSource is IERIDataSource { AuthMethod: AuthMethod.NONE or AuthMethod.KERBEROS }) + applyChanges = true; + + // All other auth methods require a secret, which we need to delete now: + else + { + var deleteSecretResponse = await this.RustService.DeleteSecret(externalDataSource); + if (deleteSecretResponse.Success) + applyChanges = true; + } + } + + if(applyChanges) + { + this.SettingsManager.ConfigurationData.DataSources.Remove(dataSource); + await this.SettingsManager.StoreSettings(); + await this.UpdateDataSources(); + await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED); + } + } + + private Task ShowInformation(IDataSource dataSource) + { + #warning Implement the information dialog for ERI data sources. + return Task.CompletedTask; + } + + private async Task UpdateDataSources() + { + this.AvailableDataSources.Clear(); + foreach (var dataSource in this.SettingsManager.ConfigurationData.DataSources) + this.AvailableDataSources.Add(new (dataSource.Name, dataSource.Id)); + + await this.AvailableDataSourcesChanged.InvokeAsync(this.AvailableDataSources); + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Dialogs/DataSourceERI_V1Dialog.razor b/app/MindWork AI Studio/Dialogs/DataSourceERI_V1Dialog.razor new file mode 100644 index 00000000..e69de29b diff --git a/app/MindWork AI Studio/Dialogs/DataSourceERI_V1Dialog.razor.cs b/app/MindWork AI Studio/Dialogs/DataSourceERI_V1Dialog.razor.cs new file mode 100644 index 00000000..5c77c72b --- /dev/null +++ b/app/MindWork AI Studio/Dialogs/DataSourceERI_V1Dialog.razor.cs @@ -0,0 +1,15 @@ +using AIStudio.Settings.DataModel; + +using Microsoft.AspNetCore.Components; + +// ReSharper disable InconsistentNaming +namespace AIStudio.Dialogs; + +public partial class DataSourceERI_V1Dialog : ComponentBase +{ + [Parameter] + public bool IsEditing { get; set; } + + [Parameter] + public DataSourceERI_V1 DataSource { get; set; } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor b/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor new file mode 100644 index 00000000..e69de29b diff --git a/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor.cs b/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor.cs new file mode 100644 index 00000000..b45c70ab --- /dev/null +++ b/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor.cs @@ -0,0 +1,14 @@ +using AIStudio.Settings.DataModel; + +using Microsoft.AspNetCore.Components; + +namespace AIStudio.Dialogs; + +public partial class DataSourceLocalDirectoryDialog : ComponentBase +{ + [Parameter] + public bool IsEditing { get; set; } + + [Parameter] + public DataSourceLocalDirectory DataSource { get; set; } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Dialogs/DataSourceLocalFileDialog.razor b/app/MindWork AI Studio/Dialogs/DataSourceLocalFileDialog.razor new file mode 100644 index 00000000..e69de29b diff --git a/app/MindWork AI Studio/Dialogs/DataSourceLocalFileDialog.razor.cs b/app/MindWork AI Studio/Dialogs/DataSourceLocalFileDialog.razor.cs new file mode 100644 index 00000000..dfd01283 --- /dev/null +++ b/app/MindWork AI Studio/Dialogs/DataSourceLocalFileDialog.razor.cs @@ -0,0 +1,14 @@ +using AIStudio.Settings.DataModel; + +using Microsoft.AspNetCore.Components; + +namespace AIStudio.Dialogs; + +public partial class DataSourceLocalFileDialog : ComponentBase +{ + [Parameter] + public bool IsEditing { get; set; } + + [Parameter] + public DataSourceLocalFile DataSource { get; set; } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Pages/Settings.razor b/app/MindWork AI Studio/Pages/Settings.razor index 4a004734..91a0df9c 100644 --- a/app/MindWork AI Studio/Pages/Settings.razor +++ b/app/MindWork AI Studio/Pages/Settings.razor @@ -7,6 +7,7 @@ + diff --git a/app/MindWork AI Studio/Pages/Settings.razor.cs b/app/MindWork AI Studio/Pages/Settings.razor.cs index d06ab02e..6baf7f14 100644 --- a/app/MindWork AI Studio/Pages/Settings.razor.cs +++ b/app/MindWork AI Studio/Pages/Settings.razor.cs @@ -11,6 +11,7 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable private List> availableLLMProviders = new(); private List> availableEmbeddingProviders = new(); + private List> availableDataSources = new(); #region Overrides of ComponentBase