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