Add ERI server assistant (#231)

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2025-01-01 15:49:27 +01:00
1 parent cdf717ad5f
commit 8cbef49d89
76 files changed
+3581 -153

No files matched your search

@@ -0,0 +1,109 @@
<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.DataEmbeddingName"
Label="Embedding Name"
HelperText="The name of the embedding method."
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Label"
AdornmentColor="Color.Info"
Validation="@this.ValidateName"
Counter="26"
MaxLength="26"
Immediate="@true"
UserAttributes="@SPELLCHECK_ATTRIBUTES"
/>
@* ReSharper disable once CSharpWarnings::CS8974 *@
<MudTextField
T="string"
@bind-Text="@this.DataEmbeddingType"
Label="Embedding Type"
HelperText="What kind of embedding is used. For example, Transformer Embedding, Contextual Word Embedding, Graph Embedding, etc."
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Extension"
AdornmentColor="Color.Info"
Counter="56"
MaxLength="56"
Validation="@this.ValidateType"
Immediate="@true"
UserAttributes="@SPELLCHECK_ATTRIBUTES"
/>
<MudList T="@string" Class="mb-3">
<MudListItem Icon="@Icons.Material.Filled.Link" Href="https://en.wikipedia.org/wiki/Word_embedding" Target="_blank" Text="See Wikipedia for more information about word embeddings"/>
<MudListItem Icon="@Icons.Material.Filled.Link" Href="https://en.wikipedia.org/wiki/Knowledge_graph_embedding" Target="_blank" Text="See Wikipedia for more information about knowledge graph embeddings"/>
</MudList>
@* ReSharper disable once CSharpWarnings::CS8974 *@
<MudTextField
T="string"
@bind-Text="@this.DataDescription"
Label="Embedding Description"
HelperText="A short description of the embedding method."
Lines="3"
AutoGrow="@true"
MaxLines="6"
Immediate="@true"
Variant="Variant.Outlined"
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Extension"
AdornmentColor="Color.Info"
Validation="@this.ValidateDescription"
UserAttributes="@SPELLCHECK_ATTRIBUTES"
/>
@* ReSharper disable once CSharpWarnings::CS8974 *@
<MudTextField
T="string"
@bind-Text="@this.DataUsedWhen"
Label="Used when"
HelperText="When is this embedding used? When you define multiple embeddings, it is helpful to know when to use which one."
Lines="3"
AutoGrow="@true"
MaxLines="6"
Immediate="@true"
Variant="Variant.Outlined"
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Assessment"
AdornmentColor="Color.Info"
Validation="@this.ValidateUsedWhen"
UserAttributes="@SPELLCHECK_ATTRIBUTES"
/>
@* ReSharper disable once CSharpWarnings::CS8974 *@
<MudTextField
T="string"
@bind-Text="@this.DataLink"
Label="Embedding Link"
HelperText="A link to the embedding, e.g., to the model, the source code, the paper, it's Wikipedia page, etc."
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Link"
AdornmentColor="Color.Info"
UserAttributes="@SPELLCHECK_ATTRIBUTES"
/>
</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>
@@ -0,0 +1,144 @@
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!;
/// <summary>
/// The user chosen embedding name.
/// </summary>
[Parameter]
public string DataEmbeddingName { get; set; } = string.Empty;
/// <summary>
/// The user chosen embedding type.
/// </summary>
[Parameter]
public string DataEmbeddingType { get; set; } = string.Empty;
/// <summary>
/// The embedding description.
/// </summary>
[Parameter]
public string DataDescription { get; set; } = string.Empty;
/// <summary>
/// When is the embedding used?
/// </summary>
[Parameter]
public string DataUsedWhen { get; set; } = string.Empty;
/// <summary>
/// A link to the embedding documentation or the source code. Might be null, which means no link is provided.
/// </summary>
[Parameter]
public string DataLink { get; set; } = string.Empty;
/// <summary>
/// The embedding method names that are already used. The user must choose a unique name.
/// </summary>
[Parameter]
public IReadOnlyList<string> UsedEmbeddingMethodNames { get; set; } = new List<string>();
/// <summary>
/// Should the dialog be in editing mode?
/// </summary>
[Parameter]
public bool IsEditing { get; init; }
[Inject]
private SettingsManager SettingsManager { get; init; } = null!;
private static readonly Dictionary<string, object?> 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.";
if (name.Length > 26)
return "The embedding name must not be longer than 26 characters.";
if (this.UsedEmbeddingMethodNames.Contains(name))
return $"The embedding method name '{name}' is already used. Please choose a unique name.";
return null;
}
private string? ValidateType(string type)
{
if (string.IsNullOrWhiteSpace(type))
return "The embedding type must not be empty. Please specify the embedding type.";
if (type.Length > 56)
return "The embedding type must not be longer than 56 characters.";
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();
}
@@ -8,7 +8,7 @@ using Host = AIStudio.Provider.SelfHosted.Host;
namespace AIStudio.Dialogs;
public partial class EmbeddingDialog : ComponentBase, ISecretId
public partial class EmbeddingProviderDialog : ComponentBase, ISecretId
{
[CascadingParameter]
private MudDialogInstance MudDialog { get; set; } = null!;
@@ -97,7 +97,7 @@ public partial class EmbeddingDialog : ComponentBase, ISecretId
private readonly Encryption encryption = Program.ENCRYPTION;
private readonly ProviderValidation providerValidation;
public EmbeddingDialog()
public EmbeddingProviderDialog()
{
this.providerValidation = new()
{
@@ -0,0 +1,213 @@
@using AIStudio.Assistants.ERI
@using MudExtensions
<MudDialog>
<DialogContent>
<MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues">
<MudText Typo="Typo.h5" Class="mb-3">
General Information
</MudText>
<MudText Typo="Typo.body1" Class="mb-3">
Please provide some general information about your retrieval process first. This data may be
displayed to the users.
</MudText>
@* ReSharper disable once CSharpWarnings::CS8974 *@
<MudTextField
T="string"
@bind-Text="@this.DataName"
Label="Retrieval Process Name"
HelperText="The name of your retrieval process."
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Label"
AdornmentColor="Color.Info"
Validation="@this.ValidateName"
Counter="26"
MaxLength="26"
Immediate="@true"
UserAttributes="@SPELLCHECK_ATTRIBUTES"
/>
@* ReSharper disable once CSharpWarnings::CS8974 *@
<MudTextField
T="string"
@bind-Text="@this.DataDescription"
Label="Retrieval Process Description"
HelperText="A short description of the retrieval process."
Lines="3"
AutoGrow="@true"
MaxLines="6"
Immediate="@true"
Variant="Variant.Outlined"
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Extension"
AdornmentColor="Color.Info"
Validation="@this.ValidateDescription"
UserAttributes="@SPELLCHECK_ATTRIBUTES"
/>
@* ReSharper disable once CSharpWarnings::CS8974 *@
<MudTextField
T="string"
@bind-Text="@this.DataLink"
Label="Retrieval Process Link"
HelperText="A link to the retrieval process, e.g., the source code, the paper, it's Wikipedia page, etc. Make sense for common retrieval processes. Leave empty if not applicable."
Class="mb-6"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Link"
AdornmentColor="Color.Info"
UserAttributes="@SPELLCHECK_ATTRIBUTES"
/>
<MudText Typo="Typo.h5" Class="mb-3">
Retrieval Process Parameters
</MudText>
<MudText Typo="Typo.body1" Class="mb-3">
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.
</MudText>
<MudStack Row="@true" Spacing="6" AlignItems="AlignItems.Start" StretchItems="StretchItems.None">
@* The left side of the stack is another stack to show the list *@
<MudStack Row="@false" AlignItems="AlignItems.Start" StretchItems="StretchItems.None">
@if (this.retrievalParameters.Count > 0)
{
<MudList T="RetrievalParameter" Class="mb-1" @bind-SelectedValue="@this.selectedParameter">
@foreach (var parameter in this.retrievalParameters)
{
<MudListItem T="RetrievalParameter" Icon="@Icons.Material.Filled.Tune" Value="@parameter">
@parameter.Name
</MudListItem>
}
</MudList>
}
<MudButton OnClick="@this.AddRetrievalProcessParameter" Variant="Variant.Filled" Color="Color.Primary" Class="mt-1">
Add Parameter
</MudButton>
</MudStack>
@* The right side of the stack is another stack to display the parameter's data *@
<MudStack Row="@false" AlignItems="AlignItems.Stretch" StretchItems="StretchItems.End" Class="pa-3 mb-8 border-solid border rounded-lg">
@if (this.selectedParameter is null)
{
@if(this.retrievalParameters.Count == 0)
{
<MudText>
Add a parameter first, then select it to edit.
</MudText>
}
else
{
<MudText>
Select a parameter to show and edit it.
</MudText>
}
}
else
{
@* ReSharper disable once CSharpWarnings::CS8974 *@
<MudTextField
T="string"
@bind-Text="@this.selectedParameter.Name"
Label="Parameter Name"
HelperText="The parameter name. It must be unique within the retrieval process."
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Label"
AdornmentColor="Color.Info"
Counter="26"
MaxLength="26"
Validation="@this.ValidateParameterName"
Immediate="@true"
UserAttributes="@SPELLCHECK_ATTRIBUTES"/>
@* ReSharper disable once CSharpWarnings::CS8974 *@
<MudTextField
T="string"
@bind-Text="@this.selectedParameter.Description"
Label="Parameter Description"
HelperText="A short description of the parameter. What data type is it? What is it used for? What are the possible values?"
Lines="3"
AutoGrow="@true"
MaxLines="6"
Immediate="@true"
Variant="Variant.Outlined"
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Extension"
AdornmentColor="Color.Info"
Validation="@this.ValidateParameterDescription"
UserAttributes="@SPELLCHECK_ATTRIBUTES"/>
<MudStack Row="@true">
<MudButton OnClick="@this.RemoveRetrievalProcessParameter" Variant="Variant.Filled" Color="Color.Secondary">
Delete this parameter
</MudButton>
</MudStack>
}
</MudStack>
</MudStack>
<MudText Typo="Typo.h5" Class="mb-3">
Embeddings
</MudText>
@if(this.AvailableEmbeddings.Count == 0)
{
<MudText Typo="Typo.body1" Class="mb-3">
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.
</MudText>
}
else
{
<MudText Typo="Typo.body1" Class="mb-3">
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.
</MudText>
<MudSelectExtended
T="EmbeddingInfo"
MultiSelection="@true"
MultiSelectionTextFunc="@this.GetMultiSelectionText"
SelectedValues="@this.DataEmbeddings"
SelectedValuesChanged="@this.EmbeddingsChanged"
Strict="@true"
Margin="Margin.Dense"
Label="Embeddings methods"
ShrinkLabel="@true"
Class="mb-3"
Variant="Variant.Outlined"
HelperText="Optional. Select the embedding methods that are used for this retrieval process.">
@foreach (var embedding in this.AvailableEmbeddings)
{
<MudSelectItemExtended Value="@embedding">
@embedding.EmbeddingName
</MudSelectItemExtended>
}
</MudSelectExtended>
}
</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>
@@ -0,0 +1,213 @@
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!;
/// <summary>
/// The user chosen retrieval process name.
/// </summary>
[Parameter]
public string DataName { get; set; } = string.Empty;
/// <summary>
/// The retrieval process description.
/// </summary>
[Parameter]
public string DataDescription { get; set; } = string.Empty;
/// <summary>
/// A link to the retrieval process documentation, paper, Wikipedia article, or the source code.
/// </summary>
[Parameter]
public string DataLink { get; set; } = string.Empty;
/// <summary>
/// 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.
/// </summary>
[Parameter]
public Dictionary<string, string> DataParametersDescription { get; set; } = new();
/// <summary>
/// A list of embeddings used in this retrieval process. It might be empty in case no embedding is used.
/// </summary>
[Parameter]
public HashSet<EmbeddingInfo> DataEmbeddings { get; set; } = new();
/// <summary>
/// The available embeddings for the user to choose from.
/// </summary>
[Parameter]
public IReadOnlyList<EmbeddingInfo> AvailableEmbeddings { get; set; } = new List<EmbeddingInfo>();
/// <summary>
/// The retrieval process names that are already used. The user must choose a unique name.
/// </summary>
[Parameter]
public IReadOnlyList<string> UsedRetrievalProcessNames { get; set; } = new List<string>();
/// <summary>
/// Should the dialog be in editing mode?
/// </summary>
[Parameter]
public bool IsEditing { get; init; }
[Inject]
private SettingsManager SettingsManager { get; init; } = null!;
private static readonly Dictionary<string, object?> SPELLCHECK_ATTRIBUTES = new();
private bool dataIsValid;
private string[] dataIssues = [];
private List<RetrievalParameter> 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.";
if (this.UsedRetrievalProcessNames.Contains(name))
return $"The retrieval process name '{name}' must be unique. Please choose a different name.";
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<EmbeddingInfo> 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<EmbeddingInfo>? 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();
}