diff --git a/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor b/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor
index c87fbbb9..56d2b73c 100644
--- a/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor
+++ b/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor
@@ -201,6 +201,44 @@
Data retrieval settings
+
+ For your ERI server, you need to retrieve data that matches a chat or prompt in some way. We call this the retrieval process.
+ You must describe at least one such process. You may offer several retrieval processes from which users can choose. This allows
+ you to test with beta users which process works better. Or you might generally want to give users the choice so they can select
+ the process that best suits their circumstances.
+
+
+
+
+
+
+
+
+ Name
+ Actions
+
+
+ @context.Name
+
+
+ Edit
+
+
+ Delete
+
+
+
+
+
+@if(this.retrievalProcesses.Count == 0)
+{
+ No retrieval process configured yet.
+}
+
+
+ Add Retrieval Process
+
+
You can integrate additional libraries. Perhaps you want to evaluate the prompts in advance using a machine learning method or analyze them with a text
mining approach? Or maybe you want to preprocess images in the prompts? For such advanced scenarios, you can specify which libraries you want to use here.
diff --git a/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor.cs b/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor.cs
index 63d7dc0c..2effc79a 100644
--- a/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor.cs
+++ b/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor.cs
@@ -64,6 +64,7 @@ public partial class AssistantERI : AssistantBaseCore
this.selectedOperatingSystem = OperatingSystem.NONE;
this.allowedLLMProviders = AllowedLLMProviders.NONE;
this.embeddings = new();
+ this.retrievalProcesses = new();
this.additionalLibraries = string.Empty;
}
}
@@ -92,6 +93,7 @@ public partial class AssistantERI : AssistantBaseCore
this.selectedOperatingSystem = this.SettingsManager.ConfigurationData.ERI.PreselectedOperatingSystem;
this.allowedLLMProviders = this.SettingsManager.ConfigurationData.ERI.PreselectedAllowedLLMProviders;
this.embeddings = this.SettingsManager.ConfigurationData.ERI.PreselectedEmbeddingInfos;
+ this.retrievalProcesses = this.SettingsManager.ConfigurationData.ERI.PreselectedRetrievalInfos;
this.additionalLibraries = this.SettingsManager.ConfigurationData.ERI.PreselectedAdditionalLibraries;
return true;
}
@@ -125,6 +127,7 @@ public partial class AssistantERI : AssistantBaseCore
this.SettingsManager.ConfigurationData.ERI.PreselectedOperatingSystem = this.selectedOperatingSystem;
this.SettingsManager.ConfigurationData.ERI.PreselectedAllowedLLMProviders = this.allowedLLMProviders;
this.SettingsManager.ConfigurationData.ERI.PreselectedEmbeddingInfos = this.embeddings;
+ this.SettingsManager.ConfigurationData.ERI.PreselectedRetrievalInfos = this.retrievalProcesses;
this.SettingsManager.ConfigurationData.ERI.PreselectedAdditionalLibraries = this.additionalLibraries;
await this.SettingsManager.StoreSettings();
}
@@ -146,6 +149,7 @@ public partial class AssistantERI : AssistantBaseCore
private OperatingSystem selectedOperatingSystem = OperatingSystem.NONE;
private AllowedLLMProviders allowedLLMProviders = AllowedLLMProviders.NONE;
private List embeddings = new();
+ private List retrievalProcesses = new();
private string additionalLibraries = string.Empty;
private string? ValidateServerName(string name)
@@ -460,6 +464,65 @@ public partial class AssistantERI : AssistantBaseCore
this.embeddings.Remove(embeddingInfo);
await this.AutoSave();
}
+
+ private async Task AddRetrievalProcess()
+ {
+ var dialogParameters = new DialogParameters
+ {
+ { x => x.IsEditing, false },
+ { x => x.AvailableEmbeddings, this.embeddings },
+ };
+
+ var dialogReference = await this.DialogService.ShowAsync("Add Retrieval Process", dialogParameters, DialogOptions.FULLSCREEN);
+ var dialogResult = await dialogReference.Result;
+ if (dialogResult is null || dialogResult.Canceled)
+ return;
+
+ var addedRetrievalProcess = (RetrievalInfo)dialogResult.Data!;
+ this.retrievalProcesses.Add(addedRetrievalProcess);
+ await this.AutoSave();
+ }
+
+ private async Task EditRetrievalProcess(RetrievalInfo retrievalInfo)
+ {
+ var dialogParameters = new DialogParameters
+ {
+ { x => x.DataName, retrievalInfo.Name },
+ { x => x.DataDescription, retrievalInfo.Description },
+ { x => x.DataLink, retrievalInfo.Link },
+ { x => x.DataParametersDescription, retrievalInfo.ParametersDescription },
+ { x => x.DataEmbeddings, retrievalInfo.Embeddings?.ToHashSet() },
+
+ { x => x.IsEditing, true },
+ { x => x.AvailableEmbeddings, this.embeddings },
+ };
+
+ var dialogReference = await this.DialogService.ShowAsync("Edit Retrieval Process", dialogParameters, DialogOptions.FULLSCREEN);
+ var dialogResult = await dialogReference.Result;
+ if (dialogResult is null || dialogResult.Canceled)
+ return;
+
+ var editedRetrievalProcess = (RetrievalInfo)dialogResult.Data!;
+
+ this.retrievalProcesses[this.retrievalProcesses.IndexOf(retrievalInfo)] = editedRetrievalProcess;
+ await this.AutoSave();
+ }
+
+ private async Task DeleteRetrievalProcess(RetrievalInfo retrievalInfo)
+ {
+ var dialogParameters = new DialogParameters
+ {
+ { "Message", $"Are you sure you want to delete the retrieval process '{retrievalInfo.Name}'?" },
+ };
+
+ var dialogReference = await this.DialogService.ShowAsync("Delete Retrieval Process", dialogParameters, DialogOptions.FULLSCREEN);
+ var dialogResult = await dialogReference.Result;
+ if (dialogResult is null || dialogResult.Canceled)
+ return;
+
+ this.retrievalProcesses.Remove(retrievalInfo);
+ await this.AutoSave();
+ }
private async Task GenerateServer()
{
diff --git a/app/MindWork AI Studio/Assistants/ERI/RetrievalInfo.cs b/app/MindWork AI Studio/Assistants/ERI/RetrievalInfo.cs
new file mode 100644
index 00000000..78ff7d2f
--- /dev/null
+++ b/app/MindWork AI Studio/Assistants/ERI/RetrievalInfo.cs
@@ -0,0 +1,18 @@
+namespace AIStudio.Assistants.ERI;
+
+///
+/// Information about a retrieval process, which this data source implements.
+///
+/// The name of the retrieval process, e.g., "Keyword-Based Wikipedia Article Retrieval".
+/// A short description of the retrieval process. What kind of retrieval process is it?
+/// A link to the retrieval process's documentation, paper, Wikipedia article, or the source code. Might be null.
+/// A dictionary that describes the parameters of the retrieval process. The key is the parameter name,
+/// and the value is a description of the parameter. Although each parameter will be sent as a string, the description should indicate the
+/// expected type and range, e.g., 0.0 to 1.0 for a float parameter.
+/// A list of embeddings used in this retrieval process. It might be empty in case no embedding is used.
+public readonly record struct RetrievalInfo(
+ string Name,
+ string Description,
+ string? Link,
+ Dictionary? ParametersDescription,
+ List? Embeddings);
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Assistants/ERI/RetrievalParameter.cs b/app/MindWork AI Studio/Assistants/ERI/RetrievalParameter.cs
new file mode 100644
index 00000000..d25a5bdd
--- /dev/null
+++ b/app/MindWork AI Studio/Assistants/ERI/RetrievalParameter.cs
@@ -0,0 +1,14 @@
+namespace AIStudio.Assistants.ERI;
+
+public sealed class RetrievalParameter
+{
+ ///
+ /// The name of the parameter.
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// The description of the parameter.
+ ///
+ public string Description { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/RetrievalProcessDialog.razor b/app/MindWork AI Studio/Dialogs/RetrievalProcessDialog.razor
new file mode 100644
index 00000000..fb7e3b00
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/RetrievalProcessDialog.razor
@@ -0,0 +1,213 @@
+@using AIStudio.Assistants.ERI
+@using MudExtensions
+
+
+
+
+
+ General Information
+
+
+
+ Please provide some general information about your retrieval process first. This data may be
+ displayed to the users.
+
+
+ @* ReSharper disable once CSharpWarnings::CS8974 *@
+
+
+ @* ReSharper disable once CSharpWarnings::CS8974 *@
+
+
+ @* ReSharper disable once CSharpWarnings::CS8974 *@
+
+
+
+ Retrieval Process Parameters
+
+
+
+ You may want to parameterize your retrieval process. However, this is optional. You can specify any
+ parameters that can be set by the user or the system during the call. Nevertheless, you should use
+ sensible default values in your code so that users are not forced to set the parameters manually.
+
+
+
+ @* The left side of the stack is another stack to show the list *@
+
+ @if (this.retrievalParameters.Count > 0)
+ {
+
+ @foreach (var parameter in this.retrievalParameters)
+ {
+
+ @parameter.Name
+
+ }
+
+ }
+
+ Add Parameter
+
+
+
+ @* The right side of the stack is another stack to display the parameter's data *@
+
+ @if (this.selectedParameter is null)
+ {
+ @if(this.retrievalParameters.Count == 0)
+ {
+
+ Add a parameter first, then select it to edit.
+
+ }
+ else
+ {
+
+ Select a parameter to show and edit it.
+
+ }
+ }
+ else
+ {
+ @* ReSharper disable once CSharpWarnings::CS8974 *@
+
+
+ @* ReSharper disable once CSharpWarnings::CS8974 *@
+
+
+
+
+ Delete this parameter
+
+
+
+ }
+
+
+
+
+ Embeddings
+
+
+ @if(this.AvailableEmbeddings.Count == 0)
+ {
+
+ Currently, you have not defined any embedding methods. If your retrieval process does not require embedding, you can ignore this part.
+ Otherwise, you can define one or more embedding methods in the previous view to assign them to your retrieval process here.
+
+ }
+ else
+ {
+
+ Here you can select which embedding methods are used for this retrieval process. Embeddings are optional;
+ if your retrieval process works without embedding, you can ignore this part. You can only choose the embedding
+ methods you have previously defined.
+
+
+
+ @foreach (var embedding in this.AvailableEmbeddings)
+ {
+
+ @embedding.EmbeddingName
+
+ }
+
+ }
+
+
+
+
+ Cancel
+
+ @if(this.IsEditing)
+ {
+ @:Update
+ }
+ else
+ {
+ @:Add
+ }
+
+
+
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/RetrievalProcessDialog.razor.cs b/app/MindWork AI Studio/Dialogs/RetrievalProcessDialog.razor.cs
new file mode 100644
index 00000000..371677b0
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/RetrievalProcessDialog.razor.cs
@@ -0,0 +1,204 @@
+using AIStudio.Assistants.ERI;
+using AIStudio.Settings;
+
+using Microsoft.AspNetCore.Components;
+
+namespace AIStudio.Dialogs;
+
+public partial class RetrievalProcessDialog : ComponentBase
+{
+ [CascadingParameter]
+ private MudDialogInstance MudDialog { get; set; } = null!;
+
+ ///
+ /// The user chosen retrieval process name.
+ ///
+ [Parameter]
+ public string DataName { get; set; } = string.Empty;
+
+ ///
+ /// The retrieval process description.
+ ///
+ [Parameter]
+ public string DataDescription { get; set; } = string.Empty;
+
+ ///
+ /// A link to the retrieval process documentation, paper, Wikipedia article, or the source code.
+ ///
+ [Parameter]
+ public string DataLink { get; set; } = string.Empty;
+
+ ///
+ /// A dictionary that describes the parameters of the retrieval process. The key is the parameter name,
+ /// and the value is a description of the parameter. Although each parameter will be sent as a string,
+ /// the description should indicate the expected type and range, e.g., 0.0 to 1.0 for a float parameter.
+ ///
+ [Parameter]
+ public Dictionary DataParametersDescription { get; set; } = new();
+
+ ///
+ /// A list of embeddings used in this retrieval process. It might be empty in case no embedding is used.
+ ///
+ [Parameter]
+ public HashSet DataEmbeddings { get; set; } = new();
+
+ ///
+ /// The available embeddings for the user to choose from.
+ ///
+ [Parameter]
+ public IReadOnlyList AvailableEmbeddings { get; set; } = new List();
+
+ ///
+ /// Should the dialog be in editing mode?
+ ///
+ [Parameter]
+ public bool IsEditing { get; init; }
+
+ [Inject]
+ private SettingsManager SettingsManager { get; init; } = null!;
+
+ private static readonly Dictionary SPELLCHECK_ATTRIBUTES = new();
+
+ private bool dataIsValid;
+ private string[] dataIssues = [];
+ private List retrievalParameters = new();
+ private RetrievalParameter? selectedParameter;
+ private uint nextParameterId = 1;
+
+ // We get the form reference from Blazor code to validate it manually:
+ private MudForm form = null!;
+
+ private RetrievalInfo CreateRetrievalInfo() => new(this.DataName, this.DataDescription, this.DataLink, this.retrievalParameters.ToDictionary(parameter => parameter.Name, parameter => parameter.Description), this.DataEmbeddings.ToList());
+
+ #region Overrides of ComponentBase
+
+ protected override async Task OnInitializedAsync()
+ {
+ // Configure the spellchecking for the instance name input:
+ this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES);
+
+ // Convert the parameters:
+ this.retrievalParameters = this.DataParametersDescription.Select(pair => new RetrievalParameter { Name = pair.Key, Description = pair.Value }).ToList();
+
+ 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 name)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ return "The retrieval process name must not be empty. Please name your retrieval process.";
+
+ if (name.Length > 26)
+ return "The retrieval process name must not be longer than 26 characters.";
+
+ return null;
+ }
+
+ private string? ValidateDescription(string description)
+ {
+ if (string.IsNullOrWhiteSpace(description))
+ return "The description must not be empty. Please describe the retrieval process.";
+
+ return null;
+ }
+
+ private void AddRetrievalProcessParameter()
+ {
+ this.retrievalParameters.Add(new() { Name = $"New Parameter {this.nextParameterId++}", Description = string.Empty });
+ }
+
+ private string? ValidateParameterName(string name)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ return "The parameter name must not be empty. Please name the parameter.";
+
+ if(name.Length > 26)
+ return "The parameter name must not be longer than 26 characters.";
+
+ if (this.retrievalParameters.Count(parameter => parameter.Name == name) > 1)
+ return $"The parameter name '{name}' must be unique. Please choose a different name.";
+
+ return null;
+ }
+
+ private string? ValidateParameterDescription(string description)
+ {
+ if (string.IsNullOrWhiteSpace(description))
+ return $"The parameter description must not be empty. Please describe the parameter '{this.selectedParameter?.Name}'. What data type is it? What is it used for? What are the possible values?";
+
+ return null;
+ }
+
+ private string? ValidateParameter(RetrievalParameter parameter)
+ {
+ if(this.ValidateParameterName(parameter.Name) is { } nameIssue)
+ return nameIssue;
+
+ if (string.IsNullOrWhiteSpace(parameter.Description))
+ return $"The parameter description must not be empty. Please describe the parameter '{parameter.Name}'. What data type is it? What is it used for? What are the possible values?";
+
+ return null;
+ }
+
+ private void RemoveRetrievalProcessParameter()
+ {
+ if (this.selectedParameter is not null)
+ this.retrievalParameters.Remove(this.selectedParameter);
+
+ this.selectedParameter = null;
+ }
+
+ private string GetMultiSelectionText(List selectedEmbeddings)
+ {
+ if(selectedEmbeddings.Count == 0)
+ return "No embedding methods selected.";
+
+ if(selectedEmbeddings.Count == 1)
+ return "You have selected 1 embedding method.";
+
+ return $"You have selected {selectedEmbeddings.Count} embedding methods.";
+ }
+
+ private void EmbeddingsChanged(IEnumerable? updatedEmbeddings)
+ {
+ if(updatedEmbeddings is null)
+ this.DataEmbeddings = new();
+ else
+ this.DataEmbeddings = updatedEmbeddings.ToHashSet();
+ }
+
+ private async Task Store()
+ {
+ await this.form.Validate();
+ foreach (var parameter in this.retrievalParameters)
+ {
+ if (this.ValidateParameter(parameter) is { } issue)
+ {
+ this.dataIsValid = false;
+ Array.Resize(ref this.dataIssues, this.dataIssues.Length + 1);
+ this.dataIssues[^1] = issue;
+ }
+ }
+
+ // When the data is not valid, we don't store it:
+ if (!this.dataIsValid || this.dataIssues.Any())
+ return;
+
+ var retrievalInfo = this.CreateRetrievalInfo();
+ this.MudDialog.Close(DialogResult.Ok(retrievalInfo));
+ }
+
+ private void Cancel() => this.MudDialog.Cancel();
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Settings/DataModel/DataERI.cs b/app/MindWork AI Studio/Settings/DataModel/DataERI.cs
index 1d58f125..4949ab14 100644
--- a/app/MindWork AI Studio/Settings/DataModel/DataERI.cs
+++ b/app/MindWork AI Studio/Settings/DataModel/DataERI.cs
@@ -98,7 +98,10 @@ public sealed class DataERI
public List PreselectedEmbeddingInfos { get; set; } = new();
///
+ /// Do you want to predefine any retrieval information?
///
+ public List PreselectedRetrievalInfos { get; set; } = new();
+
///
/// Do you want to preselect any additional libraries?
///