From 75b11ff1a3a731dcbead0dfd17ff7aba66412e7a Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Wed, 25 Dec 2024 22:50:04 +0100 Subject: [PATCH] Added embedding information --- .../Assistants/ERI/AssistantERI.razor | 51 +++++++ .../Assistants/ERI/AssistantERI.razor.cs | 66 +++++++++ .../Assistants/ERI/EmbeddingInfo.cs | 19 +++ .../Dialogs/EmbeddingMethodDialog.razor | 103 ++++++++++++++ .../Dialogs/EmbeddingMethodDialog.razor.cs | 129 ++++++++++++++++++ .../Settings/DataModel/DataERI.cs | 5 + 6 files changed, 373 insertions(+) create mode 100644 app/MindWork AI Studio/Assistants/ERI/EmbeddingInfo.cs create mode 100644 app/MindWork AI Studio/Dialogs/EmbeddingMethodDialog.razor create mode 100644 app/MindWork AI Studio/Dialogs/EmbeddingMethodDialog.razor.cs diff --git a/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor b/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor index 9b8fc5b9..0a75e399 100644 --- a/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor +++ b/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor @@ -146,6 +146,57 @@ } + + Embedding settings + + + + You will likely use one or more embedding methods to encode the meaning of your data into a typically high-dimensional vector + space. In this case, you will use a vector database to store and search these vectors (called embeddings). However, you don't + have to use embedding methods. When your retrieval method works without any embedding, you can ignore this section. An example: You + store files on a file server, and your retrieval method works exclusively with file names in the file system, so you don't + need embeddings. + + + + You can specify more than one embedding method. This can be useful when you want to use different embeddings for different queries + or data types. For example, one embedding for texts, another for images, and a third for videos, etc. + + + + + + + + + + Name + Type + Actions + + + @context.EmbeddingName + @context.EmbeddingType + + + Edit + + + Delete + + + + + +@if(this.embeddings.Count == 0) +{ + No embeddings configured yet. +} + + + Add Embedding + + Data retrieval settings diff --git a/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor.cs b/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor.cs index 1b1107a2..f50459ef 100644 --- a/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor.cs +++ b/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor.cs @@ -1,7 +1,10 @@ using AIStudio.Chat; +using AIStudio.Dialogs; using Microsoft.AspNetCore.Components; +using DialogOptions = AIStudio.Dialogs.DialogOptions; + namespace AIStudio.Assistants.ERI; public partial class AssistantERI : AssistantBaseCore @@ -9,6 +12,9 @@ public partial class AssistantERI : AssistantBaseCore [Inject] private HttpClient HttpClient { get; set; } = null!; + [Inject] + private IDialogService DialogService { get; init; } = null!; + public override Tools.Components Component => Tools.Components.ERI_ASSISTANT; protected override string Title => "ERI Server"; @@ -56,6 +62,7 @@ public partial class AssistantERI : AssistantBaseCore this.authDescription = string.Empty; this.selectedOperatingSystem = OperatingSystem.NONE; this.allowedLLMProviders = AllowedLLMProviders.NONE; + this.embeddings = new(); this.retrievalDescription = string.Empty; this.additionalLibraries = string.Empty; } @@ -83,6 +90,7 @@ public partial class AssistantERI : AssistantBaseCore this.authDescription = this.SettingsManager.ConfigurationData.ERI.PreselectedAuthDescription; this.selectedOperatingSystem = this.SettingsManager.ConfigurationData.ERI.PreselectedOperatingSystem; this.allowedLLMProviders = this.SettingsManager.ConfigurationData.ERI.PreselectedAllowedLLMProviders; + this.embeddings = this.SettingsManager.ConfigurationData.ERI.EmbeddingInfos; this.retrievalDescription = this.SettingsManager.ConfigurationData.ERI.PreselectedRetrievalDescription; this.additionalLibraries = this.SettingsManager.ConfigurationData.ERI.PreselectedAdditionalLibraries; return true; @@ -115,6 +123,7 @@ public partial class AssistantERI : AssistantBaseCore this.SettingsManager.ConfigurationData.ERI.PreselectedAuthDescription = this.authDescription; this.SettingsManager.ConfigurationData.ERI.PreselectedOperatingSystem = this.selectedOperatingSystem; this.SettingsManager.ConfigurationData.ERI.PreselectedAllowedLLMProviders = this.allowedLLMProviders; + this.SettingsManager.ConfigurationData.ERI.EmbeddingInfos = this.embeddings; this.SettingsManager.ConfigurationData.ERI.PreselectedRetrievalDescription = this.retrievalDescription; this.SettingsManager.ConfigurationData.ERI.PreselectedAdditionalLibraries = this.additionalLibraries; await this.SettingsManager.StoreSettings(); @@ -135,6 +144,7 @@ public partial class AssistantERI : AssistantBaseCore private string authDescription = string.Empty; private OperatingSystem selectedOperatingSystem = OperatingSystem.NONE; private AllowedLLMProviders allowedLLMProviders = AllowedLLMProviders.NONE; + private List embeddings = new(); private string retrievalDescription = string.Empty; private string additionalLibraries = string.Empty; @@ -389,6 +399,62 @@ public partial class AssistantERI : AssistantBaseCore return true; } } + + private async Task AddEmbedding() + { + var dialogParameters = new DialogParameters + { + { x => x.IsEditing, false }, + }; + + var dialogReference = await this.DialogService.ShowAsync("Add Embedding", dialogParameters, DialogOptions.FULLSCREEN); + var dialogResult = await dialogReference.Result; + if (dialogResult is null || dialogResult.Canceled) + return; + + var addedEmbedding = (EmbeddingInfo)dialogResult.Data!; + this.embeddings.Add(addedEmbedding); + await this.AutoSave(); + } + + private async Task EditEmbedding(EmbeddingInfo embeddingInfo) + { + var dialogParameters = new DialogParameters + { + { x => x.DataEmbeddingName, embeddingInfo.EmbeddingName }, + { x => x.DataEmbeddingType, embeddingInfo.EmbeddingType }, + { x => x.DataDescription, embeddingInfo.Description }, + { x => x.DataUsedWhen, embeddingInfo.UsedWhen }, + { x => x.DataLink, embeddingInfo.Link }, + { x => x.IsEditing, true }, + }; + + var dialogReference = await this.DialogService.ShowAsync("Edit Embedding", dialogParameters, DialogOptions.FULLSCREEN); + var dialogResult = await dialogReference.Result; + if (dialogResult is null || dialogResult.Canceled) + return; + + var editedEmbedding = (EmbeddingInfo)dialogResult.Data!; + + this.embeddings[this.embeddings.IndexOf(embeddingInfo)] = editedEmbedding; + await this.AutoSave(); + } + + private async Task DeleteEmbedding(EmbeddingInfo embeddingInfo) + { + var dialogParameters = new DialogParameters + { + { "Message", $"Are you sure you want to delete the embedding '{embeddingInfo.EmbeddingName}'?" }, + }; + + var dialogReference = await this.DialogService.ShowAsync("Delete Embedding", dialogParameters, DialogOptions.FULLSCREEN); + var dialogResult = await dialogReference.Result; + if (dialogResult is null || dialogResult.Canceled) + return; + + this.embeddings.Remove(embeddingInfo); + await this.AutoSave(); + } private async Task GenerateServer() { diff --git a/app/MindWork AI Studio/Assistants/ERI/EmbeddingInfo.cs b/app/MindWork AI Studio/Assistants/ERI/EmbeddingInfo.cs new file mode 100644 index 00000000..938ac20e --- /dev/null +++ b/app/MindWork AI Studio/Assistants/ERI/EmbeddingInfo.cs @@ -0,0 +1,19 @@ +namespace AIStudio.Assistants.ERI; + +/// +/// Represents information about the used embedding for a data source. +/// +/// What kind of embedding is used. For example, "Transformer Embedding," "Contextual Word +/// Embedding," "Graph Embedding," etc. +/// Name the embedding used. This can be a library, a framework, or the name of the used +/// algorithm. +/// A short description of the embedding. Describe what the embedding is doing. +/// Describe when the embedding is used. For example, when the user prompt contains certain +/// keywords, or anytime? +/// A link to the embedding's documentation or the source code. Might be null. +public readonly record struct EmbeddingInfo( + string EmbeddingType, + string EmbeddingName, + string Description, + string UsedWhen, + string? Link); \ No newline at end of file diff --git a/app/MindWork AI Studio/Dialogs/EmbeddingMethodDialog.razor b/app/MindWork AI Studio/Dialogs/EmbeddingMethodDialog.razor new file mode 100644 index 00000000..0fe1b64b --- /dev/null +++ b/app/MindWork AI Studio/Dialogs/EmbeddingMethodDialog.razor @@ -0,0 +1,103 @@ + + + + @* ReSharper disable once CSharpWarnings::CS8974 *@ + + + @* ReSharper disable once CSharpWarnings::CS8974 *@ + + + + + + + + @* ReSharper disable once CSharpWarnings::CS8974 *@ + + + @* ReSharper disable once CSharpWarnings::CS8974 *@ + + + @* ReSharper disable once CSharpWarnings::CS8974 *@ + + + + + + + Cancel + + @if(this.IsEditing) + { + @:Update + } + else + { + @:Add + } + + + \ No newline at end of file diff --git a/app/MindWork AI Studio/Dialogs/EmbeddingMethodDialog.razor.cs b/app/MindWork AI Studio/Dialogs/EmbeddingMethodDialog.razor.cs new file mode 100644 index 00000000..2b394f5f --- /dev/null +++ b/app/MindWork AI Studio/Dialogs/EmbeddingMethodDialog.razor.cs @@ -0,0 +1,129 @@ +using AIStudio.Assistants.ERI; +using AIStudio.Settings; + +using Microsoft.AspNetCore.Components; + +namespace AIStudio.Dialogs; + +public partial class EmbeddingMethodDialog : ComponentBase +{ + [CascadingParameter] + private MudDialogInstance MudDialog { get; set; } = null!; + + /// + /// The user chosen embedding name. + /// + [Parameter] + public string DataEmbeddingName { get; set; } = string.Empty; + + /// + /// The user chosen embedding type. + /// + [Parameter] + public string DataEmbeddingType { get; set; } = string.Empty; + + /// + /// The embedding description. + /// + [Parameter] + public string DataDescription { get; set; } = string.Empty; + + /// + /// When is the embedding used? + /// + [Parameter] + public string DataUsedWhen { get; set; } = string.Empty; + + /// + /// A link to the embedding documentation or the source code. Might be null, which means no link is provided. + /// + [Parameter] + public string? DataLink { get; set; } + + /// + /// 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 = []; + + // We get the form reference from Blazor code to validate it manually: + private MudForm form = null!; + + private EmbeddingInfo CreateEmbeddingInfo() => new(this.DataEmbeddingType, this.DataEmbeddingName, this.DataDescription, this.DataUsedWhen, this.DataLink); + + #region Overrides of ComponentBase + + protected override async Task OnInitializedAsync() + { + // Configure the spellchecking for the instance name input: + this.SettingsManager.InjectSpellchecking(SPELLCHECK_ATTRIBUTES); + + 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 embedding name must not be empty. Please name the embedding."; + + return null; + } + + private string? ValidateType(string type) + { + if (string.IsNullOrWhiteSpace(type)) + return "The embedding type must not be empty. Please specify the embedding type."; + + return null; + } + + private string? ValidateDescription(string description) + { + if (string.IsNullOrWhiteSpace(description)) + return "The description must not be empty. Please describe the embedding method."; + + return null; + } + + private string? ValidateUsedWhen(string usedWhen) + { + if (string.IsNullOrWhiteSpace(usedWhen)) + return "Please describe when the embedding is used. Might be anytime or when certain keywords are present, etc."; + + return null; + } + + private async Task Store() + { + await this.form.Validate(); + + // When the data is not valid, we don't store it: + if (!this.dataIsValid) + return; + + var embeddingInfo = this.CreateEmbeddingInfo(); + this.MudDialog.Close(DialogResult.Ok(embeddingInfo)); + } + + 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 9e389f4b..bbdc779a 100644 --- a/app/MindWork AI Studio/Settings/DataModel/DataERI.cs +++ b/app/MindWork AI Studio/Settings/DataModel/DataERI.cs @@ -87,6 +87,11 @@ public sealed class DataERI /// public AllowedLLMProviders PreselectedAllowedLLMProviders { get; set; } = AllowedLLMProviders.NONE; + /// + /// Do you want to predefine any embedding information? + /// + public List EmbeddingInfos { get; set; } = new(); + /// /// Do you want to preselect a retrieval description? ///