Refactored embedding settings

This commit is contained in:
Thorsten Sommer 2025-01-05 14:33:34 +01:00
parent 64e32cd1fa
commit d8db4f0dce
Signed by: tsommer
GPG Key ID: 371BBA77A02C0108
5 changed files with 188 additions and 167 deletions

View File

@ -17,4 +17,7 @@ public abstract class SettingsPanelBase : ComponentBase
[Inject] [Inject]
protected MessageBus MessageBus { get; init; } = null!; protected MessageBus MessageBus { get; init; } = null!;
[Inject]
protected RustService RustService { get; init; } = null!;
} }

View File

@ -0,0 +1,70 @@
@using AIStudio.Provider
@using AIStudio.Settings.DataModel
@inherits SettingsPanelBase
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
{
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.IntegrationInstructions" HeaderText="Configure Embeddings">
<PreviewPrototype/>
<MudText Typo="Typo.h4" Class="mb-3">
Configured Embeddings
</MudText>
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
Embeddings are a way to represent words, sentences, entire documents, or even images and videos as digital
fingerprints. Just like each person has a unique fingerprint, embedding models create unique digital patterns
that capture the meaning and characteristics of the content they analyze. When two things are similar in meaning
or content, their digital fingerprints will look very similar. For example, the fingerprints for 'happy' and
'joyful' would be more alike than those for 'happy' and 'sad'.
</MudJustifiedText>
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
This helps AI Studio understand and compare things in a way that's similar to how humans do. When you're working on
something, AI Studio can automatically identify related documents and data by comparing their digital fingerprints.
For instance, if you're writing about customer service, AI Studio can instantly find other documents in your data that
discuss similar topics or experiences, even if they use different words.
</MudJustifiedText>
<MudTable Items="@this.SettingsManager.ConfigurationData.EmbeddingProviders" Hover="@true" Class="border-dashed border rounded-lg">
<ColGroup>
<col style="width: 3em;"/>
<col style="width: 12em;"/>
<col style="width: 12em;"/>
<col/>
<col style="width: 40em;"/>
</ColGroup>
<HeaderContent>
<MudTh>#</MudTh>
<MudTh>Name</MudTh>
<MudTh>Provider</MudTh>
<MudTh>Model</MudTh>
<MudTh Style="text-align: left;">Actions</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd>@context.Num</MudTd>
<MudTd>@context.Name</MudTd>
<MudTd>@context.UsedLLMProvider</MudTd>
<MudTd>@this.GetEmbeddingProviderModelName(context)</MudTd>
<MudTd Style="text-align: left;">
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.OpenInBrowser" Class="ma-2" Href="@context.UsedLLMProvider.GetDashboardURL()" Target="_blank" Disabled="@(!context.UsedLLMProvider.HasDashboard())">
Open Dashboard
</MudButton>
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditEmbeddingProvider(context)">
Edit
</MudButton>
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteEmbeddingProvider(context)">
Delete
</MudButton>
</MudTd>
</RowTemplate>
</MudTable>
@if (this.SettingsManager.ConfigurationData.EmbeddingProviders.Count == 0)
{
<MudText Typo="Typo.h6" Class="mt-3">No embeddings configured yet.</MudText>
}
<MudButton Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" Class="mt-3 mb-6" OnClick="@this.AddEmbeddingProvider">
Add Embedding
</MudButton>
</ExpansionPanel>
}

View File

@ -0,0 +1,112 @@
using AIStudio.Dialogs;
using AIStudio.Settings;
using Microsoft.AspNetCore.Components;
using DialogOptions = AIStudio.Dialogs.DialogOptions;
namespace AIStudio.Components.Settings;
public partial class SettingsPanelEmbeddings : SettingsPanelBase
{
[Parameter]
public List<ConfigurationSelectData<string>> AvailableEmbeddingProviders { get; set; } = new();
[Parameter]
public EventCallback<List<ConfigurationSelectData<string>>> AvailableEmbeddingProvidersChanged { get; set; }
private string GetEmbeddingProviderModelName(EmbeddingProvider provider)
{
const int MAX_LENGTH = 36;
var modelName = provider.Model.ToString();
return modelName.Length > MAX_LENGTH ? "[...] " + modelName[^Math.Min(MAX_LENGTH, modelName.Length)..] : modelName;
}
private async Task AddEmbeddingProvider()
{
var dialogParameters = new DialogParameters<EmbeddingProviderDialog>
{
{ x => x.IsEditing, false },
};
var dialogReference = await this.DialogService.ShowAsync<EmbeddingProviderDialog>("Add Embedding Provider", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled)
return;
var addedEmbedding = (EmbeddingProvider)dialogResult.Data!;
addedEmbedding = addedEmbedding with { Num = this.SettingsManager.ConfigurationData.NextEmbeddingNum++ };
this.SettingsManager.ConfigurationData.EmbeddingProviders.Add(addedEmbedding);
await this.UpdateEmbeddingProviders();
await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}
private async Task EditEmbeddingProvider(EmbeddingProvider embeddingProvider)
{
var dialogParameters = new DialogParameters<EmbeddingProviderDialog>
{
{ x => x.DataNum, embeddingProvider.Num },
{ x => x.DataId, embeddingProvider.Id },
{ x => x.DataName, embeddingProvider.Name },
{ x => x.DataLLMProvider, embeddingProvider.UsedLLMProvider },
{ x => x.DataModel, embeddingProvider.Model },
{ x => x.DataHostname, embeddingProvider.Hostname },
{ x => x.IsSelfHosted, embeddingProvider.IsSelfHosted },
{ x => x.IsEditing, true },
{ x => x.DataHost, embeddingProvider.Host },
};
var dialogReference = await this.DialogService.ShowAsync<EmbeddingProviderDialog>("Edit Embedding Provider", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled)
return;
var editedEmbeddingProvider = (EmbeddingProvider)dialogResult.Data!;
// Set the provider number if it's not set. This is important for providers
// added before we started saving the provider number.
if(editedEmbeddingProvider.Num == 0)
editedEmbeddingProvider = editedEmbeddingProvider with { Num = this.SettingsManager.ConfigurationData.NextEmbeddingNum++ };
this.SettingsManager.ConfigurationData.EmbeddingProviders[this.SettingsManager.ConfigurationData.EmbeddingProviders.IndexOf(embeddingProvider)] = editedEmbeddingProvider;
await this.UpdateEmbeddingProviders();
await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}
private async Task DeleteEmbeddingProvider(EmbeddingProvider provider)
{
var dialogParameters = new DialogParameters
{
{ "Message", $"Are you sure you want to delete the embedding provider '{provider.Name}'?" },
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Embedding Provider", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled)
return;
var deleteSecretResponse = await this.RustService.DeleteAPIKey(provider);
if(deleteSecretResponse.Success)
{
this.SettingsManager.ConfigurationData.EmbeddingProviders.Remove(provider);
await this.SettingsManager.StoreSettings();
}
await this.UpdateEmbeddingProviders();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}
private async Task UpdateEmbeddingProviders()
{
this.AvailableEmbeddingProviders.Clear();
foreach (var provider in this.SettingsManager.ConfigurationData.EmbeddingProviders)
this.AvailableEmbeddingProviders.Add(new (provider.Name, provider.Id));
await this.AvailableEmbeddingProvidersChanged.InvokeAsync(this.AvailableEmbeddingProviders);
}
}

View File

@ -1,7 +1,6 @@
@attribute [Route(Routes.SETTINGS)] @attribute [Route(Routes.SETTINGS)]
@using AIStudio.Provider @using AIStudio.Provider
@using AIStudio.Settings @using AIStudio.Settings
@using AIStudio.Settings.DataModel
@using AIStudio.Components.Settings @using AIStudio.Components.Settings
@using Host = AIStudio.Provider.SelfHosted.Host @using Host = AIStudio.Provider.SelfHosted.Host
@ -130,73 +129,7 @@
</ExpansionPanel> </ExpansionPanel>
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager)) <SettingsPanelEmbeddings AvailableLLMProvidersFunc="() => this.availableLLMProviders" @bind-AvailableEmbeddingProviders="@this.availableEmbeddingProviders" />
{
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.IntegrationInstructions" HeaderText="Configure Embeddings">
<PreviewPrototype/>
<MudText Typo="Typo.h4" Class="mb-3">
Configured Embeddings
</MudText>
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
Embeddings are a way to represent words, sentences, entire documents, or even images and videos as digital
fingerprints. Just like each person has a unique fingerprint, embedding models create unique digital patterns
that capture the meaning and characteristics of the content they analyze. When two things are similar in meaning
or content, their digital fingerprints will look very similar. For example, the fingerprints for 'happy' and
'joyful' would be more alike than those for 'happy' and 'sad'.
</MudJustifiedText>
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
This helps AI Studio understand and compare things in a way that's similar to how humans do. When you're working on
something, AI Studio can automatically identify related documents and data by comparing their digital fingerprints.
For instance, if you're writing about customer service, AI Studio can instantly find other documents in your data that
discuss similar topics or experiences, even if they use different words.
</MudJustifiedText>
<MudTable Items="@this.SettingsManager.ConfigurationData.EmbeddingProviders" Hover="@true" Class="border-dashed border rounded-lg">
<ColGroup>
<col style="width: 3em;"/>
<col style="width: 12em;"/>
<col style="width: 12em;"/>
<col/>
<col style="width: 40em;"/>
</ColGroup>
<HeaderContent>
<MudTh>#</MudTh>
<MudTh>Name</MudTh>
<MudTh>Provider</MudTh>
<MudTh>Model</MudTh>
<MudTh Style="text-align: left;">Actions</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd>@context.Num</MudTd>
<MudTd>@context.Name</MudTd>
<MudTd>@context.UsedLLMProvider</MudTd>
<MudTd>@this.GetEmbeddingProviderModelName(context)</MudTd>
<MudTd Style="text-align: left;">
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.OpenInBrowser" Class="ma-2" Href="@context.UsedLLMProvider.GetDashboardURL()" Target="_blank" Disabled="@(!context.UsedLLMProvider.HasDashboard())">
Open Dashboard
</MudButton>
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.Edit" Class="ma-2" OnClick="() => this.EditEmbeddingProvider(context)">
Edit
</MudButton>
<MudButton Variant="Variant.Filled" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" Class="ma-2" OnClick="() => this.DeleteEmbeddingProvider(context)">
Delete
</MudButton>
</MudTd>
</RowTemplate>
</MudTable>
@if (this.SettingsManager.ConfigurationData.EmbeddingProviders.Count == 0)
{
<MudText Typo="Typo.h6" Class="mt-3">No embeddings configured yet.</MudText>
}
<MudButton Variant="Variant.Filled" Color="@Color.Primary" StartIcon="@Icons.Material.Filled.AddRoad" Class="mt-3 mb-6" OnClick="@this.AddEmbeddingProvider">
Add Embedding
</MudButton>
</ExpansionPanel>
}
<SettingsPanelProfiles AvailableLLMProvidersFunc="() => this.availableLLMProviders" /> <SettingsPanelProfiles AvailableLLMProvidersFunc="() => this.availableLLMProviders" />
<SettingsPanelApp AvailableLLMProvidersFunc="() => this.availableLLMProviders" /> <SettingsPanelApp AvailableLLMProvidersFunc="() => this.availableLLMProviders" />
<SettingsPanelChat AvailableLLMProvidersFunc="() => this.availableLLMProviders" /> <SettingsPanelChat AvailableLLMProvidersFunc="() => this.availableLLMProviders" />

View File

@ -26,7 +26,7 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
private RustService RustService { get; init; } = null!; private RustService RustService { get; init; } = null!;
private readonly List<ConfigurationSelectData<string>> availableLLMProviders = new(); private readonly List<ConfigurationSelectData<string>> availableLLMProviders = new();
private readonly List<ConfigurationSelectData<string>> availableEmbeddingProviders = new(); private List<ConfigurationSelectData<string>> availableEmbeddingProviders = new();
#region Overrides of ComponentBase #region Overrides of ComponentBase
@ -161,103 +161,6 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
#endregion #endregion
#region Embedding provider related
private string GetEmbeddingProviderModelName(EmbeddingProvider provider)
{
const int MAX_LENGTH = 36;
var modelName = provider.Model.ToString();
return modelName.Length > MAX_LENGTH ? "[...] " + modelName[^Math.Min(MAX_LENGTH, modelName.Length)..] : modelName;
}
private async Task AddEmbeddingProvider()
{
var dialogParameters = new DialogParameters<EmbeddingProviderDialog>
{
{ x => x.IsEditing, false },
};
var dialogReference = await this.DialogService.ShowAsync<EmbeddingProviderDialog>("Add Embedding Provider", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled)
return;
var addedEmbedding = (EmbeddingProvider)dialogResult.Data!;
addedEmbedding = addedEmbedding with { Num = this.SettingsManager.ConfigurationData.NextEmbeddingNum++ };
this.SettingsManager.ConfigurationData.EmbeddingProviders.Add(addedEmbedding);
this.UpdateEmbeddingProviders();
await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}
private async Task EditEmbeddingProvider(EmbeddingProvider embeddingProvider)
{
var dialogParameters = new DialogParameters<EmbeddingProviderDialog>
{
{ x => x.DataNum, embeddingProvider.Num },
{ x => x.DataId, embeddingProvider.Id },
{ x => x.DataName, embeddingProvider.Name },
{ x => x.DataLLMProvider, embeddingProvider.UsedLLMProvider },
{ x => x.DataModel, embeddingProvider.Model },
{ x => x.DataHostname, embeddingProvider.Hostname },
{ x => x.IsSelfHosted, embeddingProvider.IsSelfHosted },
{ x => x.IsEditing, true },
{ x => x.DataHost, embeddingProvider.Host },
};
var dialogReference = await this.DialogService.ShowAsync<EmbeddingProviderDialog>("Edit Embedding Provider", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled)
return;
var editedEmbeddingProvider = (EmbeddingProvider)dialogResult.Data!;
// Set the provider number if it's not set. This is important for providers
// added before we started saving the provider number.
if(editedEmbeddingProvider.Num == 0)
editedEmbeddingProvider = editedEmbeddingProvider with { Num = this.SettingsManager.ConfigurationData.NextEmbeddingNum++ };
this.SettingsManager.ConfigurationData.EmbeddingProviders[this.SettingsManager.ConfigurationData.EmbeddingProviders.IndexOf(embeddingProvider)] = editedEmbeddingProvider;
this.UpdateEmbeddingProviders();
await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}
private async Task DeleteEmbeddingProvider(EmbeddingProvider provider)
{
var dialogParameters = new DialogParameters
{
{ "Message", $"Are you sure you want to delete the embedding provider '{provider.Name}'?" },
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>("Delete Embedding Provider", dialogParameters, DialogOptions.FULLSCREEN);
var dialogResult = await dialogReference.Result;
if (dialogResult is null || dialogResult.Canceled)
return;
var deleteSecretResponse = await this.RustService.DeleteAPIKey(provider);
if(deleteSecretResponse.Success)
{
this.SettingsManager.ConfigurationData.EmbeddingProviders.Remove(provider);
await this.SettingsManager.StoreSettings();
}
this.UpdateEmbeddingProviders();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}
private void UpdateEmbeddingProviders()
{
this.availableEmbeddingProviders.Clear();
foreach (var provider in this.SettingsManager.ConfigurationData.EmbeddingProviders)
this.availableEmbeddingProviders.Add(new (provider.Name, provider.Id));
}
#endregion
#region Implementation of IMessageBusReceiver #region Implementation of IMessageBusReceiver
public Task ProcessMessage<TMsg>(ComponentBase? sendingComponent, Event triggeredEvent, TMsg? data) public Task ProcessMessage<TMsg>(ComponentBase? sendingComponent, Event triggeredEvent, TMsg? data)