AI-Studio/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor.cs

472 lines
19 KiB
C#
Raw Normal View History

2024-12-04 12:49:31 +00:00
using AIStudio.Chat;
2024-12-25 21:50:04 +00:00
using AIStudio.Dialogs;
2024-12-04 12:49:31 +00:00
2024-12-09 18:25:22 +00:00
using Microsoft.AspNetCore.Components;
2024-12-25 21:50:04 +00:00
using DialogOptions = AIStudio.Dialogs.DialogOptions;
2024-12-14 18:48:47 +00:00
namespace AIStudio.Assistants.ERI;
2024-12-04 12:49:31 +00:00
2024-12-14 18:48:47 +00:00
public partial class AssistantERI : AssistantBaseCore
2024-12-04 12:49:31 +00:00
{
2024-12-09 18:25:22 +00:00
[Inject]
private HttpClient HttpClient { get; set; } = null!;
2024-12-25 21:50:04 +00:00
[Inject]
private IDialogService DialogService { get; init; } = null!;
2024-12-14 18:48:47 +00:00
public override Tools.Components Component => Tools.Components.ERI_ASSISTANT;
2024-12-04 12:49:31 +00:00
2024-12-14 18:48:47 +00:00
protected override string Title => "ERI Server";
2024-12-04 12:49:31 +00:00
protected override string Description =>
"""
2024-12-14 18:48:47 +00:00
The ERI is the External Retrieval Interface for AI Studio and other tools. The ERI acts as a contract
between decentralized data sources and, e.g., AI Studio. The ERI is implemented by the data sources,
allowing them to be integrated into AI Studio later. This means that the data sources assume the server
role and AI Studio (or any other LLM tool) assumes the client role of the API. This approach serves to
realize a Retrieval-Augmented Generation (RAG) process with external data.
2024-12-04 12:49:31 +00:00
""";
protected override string SystemPrompt =>
$"""
""";
protected override IReadOnlyList<IButtonData> FooterButtons => [];
2024-12-14 18:48:47 +00:00
protected override string SubmitText => "Create the ERI server";
2024-12-04 12:49:31 +00:00
protected override Func<Task> SubmitAction => this.GenerateServer;
2024-12-04 12:49:31 +00:00
protected override ChatThread ConvertToChatThread => (this.chatThread ?? new()) with
{
SystemPrompt = SystemPrompts.DEFAULT,
};
protected override void ResetFrom()
{
if (!this.MightPreselectValues())
{
2024-12-09 19:11:37 +00:00
this.serverName = string.Empty;
this.serverDescription = string.Empty;
2024-12-14 18:48:47 +00:00
this.selectedERIVersion = ERIVersion.V1;
this.selectedProgrammingLanguage = ProgrammingLanguages.NONE;
this.otherProgrammingLanguage = string.Empty;
this.selectedDataSource = DataSources.NONE;
this.dataSourceProductName = string.Empty;
this.otherDataSource = string.Empty;
this.dataSourceHostname = string.Empty;
this.dataSourcePort = null;
this.selectedAuthenticationMethods = [];
this.authDescription = string.Empty;
this.selectedOperatingSystem = OperatingSystem.NONE;
this.allowedLLMProviders = AllowedLLMProviders.NONE;
2024-12-25 21:50:04 +00:00
this.embeddings = new();
this.retrievalDescription = string.Empty;
this.additionalLibraries = string.Empty;
}
2024-12-04 12:49:31 +00:00
}
protected override bool MightPreselectValues()
{
2024-12-19 10:51:38 +00:00
this.autoSave = this.SettingsManager.ConfigurationData.ERI.AutoSaveChanges;
2024-12-14 18:48:47 +00:00
if (this.SettingsManager.ConfigurationData.ERI.PreselectOptions)
{
2024-12-14 18:48:47 +00:00
this.serverName = this.SettingsManager.ConfigurationData.ERI.PreselectedServerName;
this.serverDescription = this.SettingsManager.ConfigurationData.ERI.PreselectedServerDescription;
this.selectedERIVersion = this.SettingsManager.ConfigurationData.ERI.PreselectedERIVersion;
this.selectedProgrammingLanguage = this.SettingsManager.ConfigurationData.ERI.PreselectedProgrammingLanguage;
this.otherProgrammingLanguage = this.SettingsManager.ConfigurationData.ERI.PreselectedOtherProgrammingLanguage;
this.selectedDataSource = this.SettingsManager.ConfigurationData.ERI.PreselectedDataSource;
this.dataSourceProductName = this.SettingsManager.ConfigurationData.ERI.PreselectedDataSourceProductName;
this.otherDataSource = this.SettingsManager.ConfigurationData.ERI.PreselectedOtherDataSource;
this.dataSourceHostname = this.SettingsManager.ConfigurationData.ERI.PreselectedDataSourceHostname;
this.dataSourcePort = this.SettingsManager.ConfigurationData.ERI.PreselectedDataSourcePort;
2024-12-14 18:48:47 +00:00
var authMethods = new HashSet<Auth>(this.SettingsManager.ConfigurationData.ERI.PreselectedAuthMethods);
this.selectedAuthenticationMethods = authMethods;
2024-12-14 18:48:47 +00:00
this.authDescription = this.SettingsManager.ConfigurationData.ERI.PreselectedAuthDescription;
this.selectedOperatingSystem = this.SettingsManager.ConfigurationData.ERI.PreselectedOperatingSystem;
this.allowedLLMProviders = this.SettingsManager.ConfigurationData.ERI.PreselectedAllowedLLMProviders;
2024-12-25 21:50:04 +00:00
this.embeddings = this.SettingsManager.ConfigurationData.ERI.EmbeddingInfos;
2024-12-14 18:48:47 +00:00
this.retrievalDescription = this.SettingsManager.ConfigurationData.ERI.PreselectedRetrievalDescription;
this.additionalLibraries = this.SettingsManager.ConfigurationData.ERI.PreselectedAdditionalLibraries;
return true;
}
2024-12-04 12:49:31 +00:00
return false;
}
2024-12-04 14:15:59 +00:00
2024-12-19 10:51:38 +00:00
protected override async Task OnFormChange()
{
await this.AutoSave();
}
private async Task AutoSave()
{
if(!this.autoSave)
return;
this.SettingsManager.ConfigurationData.ERI.PreselectedServerName = this.serverName;
this.SettingsManager.ConfigurationData.ERI.PreselectedServerDescription = this.serverDescription;
this.SettingsManager.ConfigurationData.ERI.PreselectedERIVersion = this.selectedERIVersion;
this.SettingsManager.ConfigurationData.ERI.PreselectedProgrammingLanguage = this.selectedProgrammingLanguage;
this.SettingsManager.ConfigurationData.ERI.PreselectedOtherProgrammingLanguage = this.otherProgrammingLanguage;
this.SettingsManager.ConfigurationData.ERI.PreselectedDataSource = this.selectedDataSource;
this.SettingsManager.ConfigurationData.ERI.PreselectedDataSourceProductName = this.dataSourceProductName;
this.SettingsManager.ConfigurationData.ERI.PreselectedOtherDataSource = this.otherDataSource;
this.SettingsManager.ConfigurationData.ERI.PreselectedDataSourceHostname = this.dataSourceHostname;
this.SettingsManager.ConfigurationData.ERI.PreselectedDataSourcePort = this.dataSourcePort;
this.SettingsManager.ConfigurationData.ERI.PreselectedAuthMethods = [..this.selectedAuthenticationMethods];
this.SettingsManager.ConfigurationData.ERI.PreselectedAuthDescription = this.authDescription;
this.SettingsManager.ConfigurationData.ERI.PreselectedOperatingSystem = this.selectedOperatingSystem;
this.SettingsManager.ConfigurationData.ERI.PreselectedAllowedLLMProviders = this.allowedLLMProviders;
2024-12-25 21:50:04 +00:00
this.SettingsManager.ConfigurationData.ERI.EmbeddingInfos = this.embeddings;
2024-12-19 10:51:38 +00:00
this.SettingsManager.ConfigurationData.ERI.PreselectedRetrievalDescription = this.retrievalDescription;
this.SettingsManager.ConfigurationData.ERI.PreselectedAdditionalLibraries = this.additionalLibraries;
await this.SettingsManager.StoreSettings();
}
private bool autoSave;
2024-12-09 19:11:37 +00:00
private string serverName = string.Empty;
private string serverDescription = string.Empty;
2024-12-14 18:48:47 +00:00
private ERIVersion selectedERIVersion = ERIVersion.V1;
2024-12-04 14:15:59 +00:00
private ProgrammingLanguages selectedProgrammingLanguage = ProgrammingLanguages.NONE;
private string otherProgrammingLanguage = string.Empty;
2024-12-04 20:19:59 +00:00
private DataSources selectedDataSource = DataSources.NONE;
private string otherDataSource = string.Empty;
private string dataSourceProductName = string.Empty;
private string dataSourceHostname = string.Empty;
private int? dataSourcePort;
2024-12-04 20:22:39 +00:00
private IEnumerable<Auth> selectedAuthenticationMethods = new HashSet<Auth>();
private string authDescription = string.Empty;
private OperatingSystem selectedOperatingSystem = OperatingSystem.NONE;
private AllowedLLMProviders allowedLLMProviders = AllowedLLMProviders.NONE;
2024-12-25 21:50:04 +00:00
private List<EmbeddingInfo> embeddings = new();
private string retrievalDescription = string.Empty;
private string additionalLibraries = string.Empty;
2024-12-04 14:15:59 +00:00
2024-12-09 19:11:37 +00:00
private string? ValidateServerName(string name)
{
if(string.IsNullOrWhiteSpace(name))
2024-12-14 18:48:47 +00:00
return "Please provide a name for your ERI server. This name will be used to identify the server in AI Studio.";
2024-12-09 19:11:37 +00:00
if(name.Length is > 60 or < 6)
2024-12-14 18:48:47 +00:00
return "The name of your ERI server must be between 6 and 60 characters long.";
2024-12-09 19:11:37 +00:00
return null;
}
private string? ValidateServerDescription(string description)
{
if(string.IsNullOrWhiteSpace(description))
2024-12-14 18:48:47 +00:00
return "Please provide a description for your ERI server. What data will the server retrieve? This description will be used to inform users about the purpose of your ERI server.";
2024-12-09 19:11:37 +00:00
if(description.Length is < 32 or > 512)
2024-12-14 18:48:47 +00:00
return "The description of your ERI server must be between 32 and 512 characters long.";
2024-12-09 19:11:37 +00:00
return null;
}
2024-12-14 18:48:47 +00:00
private string? ValidateERIVersion(ERIVersion version)
2024-12-09 18:25:22 +00:00
{
2024-12-14 18:48:47 +00:00
if (version == ERIVersion.NONE)
return "Please select an ERI specification version for the ERI server.";
2024-12-09 18:25:22 +00:00
return null;
}
2024-12-04 14:15:59 +00:00
private string? ValidateProgrammingLanguage(ProgrammingLanguages language)
{
if (language == ProgrammingLanguages.OTHER)
return null;
if (language == ProgrammingLanguages.NONE)
2024-12-14 18:48:47 +00:00
return "Please select a programming language for the ERI server.";
2024-12-04 14:15:59 +00:00
return null;
}
private string? ValidateOtherLanguage(string language)
{
if(this.selectedProgrammingLanguage != ProgrammingLanguages.OTHER)
return null;
if(string.IsNullOrWhiteSpace(language))
2024-12-14 18:48:47 +00:00
return "Please specify the custom programming language for the ERI server.";
2024-12-04 14:15:59 +00:00
return null;
}
2024-12-04 20:19:59 +00:00
private string? ValidateDataSource(DataSources dataSource)
{
if (dataSource == DataSources.CUSTOM)
return null;
if (dataSource == DataSources.NONE)
2024-12-14 18:48:47 +00:00
return "Please select a data source for the ERI server.";
2024-12-04 20:19:59 +00:00
return null;
}
private string? ValidateDataSourceProductName(string productName)
{
if(this.selectedDataSource is DataSources.CUSTOM or DataSources.NONE or DataSources.FILE_SYSTEM)
return null;
if(string.IsNullOrWhiteSpace(productName))
return "Please specify the product name of the data source, e.g., 'MongoDB', 'Redis', 'PostgreSQL', 'Neo4j', or 'MinIO', etc.";
return null;
}
2024-12-04 20:19:59 +00:00
private string? ValidateOtherDataSource(string dataSource)
{
if(this.selectedDataSource != DataSources.CUSTOM)
return null;
if(string.IsNullOrWhiteSpace(dataSource))
2024-12-14 18:48:47 +00:00
return "Please describe the data source of your ERI server.";
2024-12-04 20:19:59 +00:00
return null;
}
2024-12-04 20:22:39 +00:00
private string? ValidateHostname(string hostname)
{
if(!this.NeedHostnamePort())
return null;
// When using a custom data source, the hostname is optional:
if(this.selectedDataSource is DataSources.CUSTOM)
return null;
if(string.IsNullOrWhiteSpace(hostname))
2024-12-14 18:48:47 +00:00
return "Please provide the hostname of the data source. Use 'localhost' if the data source is on the same machine as the ERI server.";
if(hostname.Length > 255)
return "The hostname of the data source must not exceed 255 characters.";
return null;
}
private string? ValidatePort(int? port)
{
if(!this.NeedHostnamePort())
return null;
// When using a custom data source, the port is optional:
if(this.selectedDataSource is DataSources.CUSTOM)
return null;
if(port is null)
return "Please provide the port of the data source.";
if(port is < 1 or > 65535)
return "The port of the data source must be between 1 and 65535.";
return null;
}
private void DataSourceWasChanged()
{
2024-12-14 18:48:47 +00:00
if(this.SettingsManager.ConfigurationData.ERI.PreselectedDataSourcePort is not null)
return;
//
// Preselect the default port for the selected data source
//
this.dataSourcePort = this.selectedDataSource switch
{
DataSources.DOCUMENT_STORE => 27017,
DataSources.KEY_VALUE_STORE => 6379,
DataSources.OBJECT_STORAGE => 9000,
DataSources.RELATIONAL_DATABASE => 5432,
DataSources.GRAPH_DATABASE => 7687,
_ => null
};
}
2024-12-04 20:22:39 +00:00
private string? ValidateAuthenticationMethods(Auth _)
{
var authenticationMethods = (this.selectedAuthenticationMethods as HashSet<Auth>)!;
if(authenticationMethods.Count == 0)
2024-12-14 18:48:47 +00:00
return "Please select at least one authentication method for the ERI server.";
2024-12-04 20:22:39 +00:00
return null;
}
private void AuthenticationMethodWasChanged(IEnumerable<Auth>? selectedValues)
{
if(selectedValues is null)
{
this.selectedAuthenticationMethods = [];
this.selectedOperatingSystem = OperatingSystem.NONE;
return;
}
this.selectedAuthenticationMethods = selectedValues;
if(!this.IsUsingKerberos())
this.selectedOperatingSystem = OperatingSystem.NONE;
}
private bool IsUsingKerberos()
{
return this.selectedAuthenticationMethods.Contains(Auth.KERBEROS);
}
private string? ValidateOperatingSystem(OperatingSystem os)
{
if(!this.IsUsingKerberos())
return null;
if(os is OperatingSystem.NONE)
2024-12-14 18:48:47 +00:00
return "Please select the operating system on which the ERI server will run. This is necessary when using SSO with Kerberos.";
return null;
}
private string? ValidateAllowedLLMProviders(AllowedLLMProviders provider)
{
if(provider == AllowedLLMProviders.NONE)
return "Please select which types of LLMs users are allowed to use with the data from this ERI server.";
return null;
}
private string AuthDescriptionTitle()
{
const string TITLE = "Describe how you planned the authentication process";
return this.IsAuthDescriptionOptional() ? $"(Optional) {TITLE}" : TITLE;
}
private bool IsAuthDescriptionOptional()
{
2024-12-19 10:51:38 +00:00
if (this.selectedAuthenticationMethods is not HashSet<Auth> authenticationMethods)
return true;
if(authenticationMethods.Count > 1)
return false;
if (authenticationMethods.Any(n => n == Auth.NONE) && authenticationMethods.Count > 1)
return false;
return true;
}
2024-12-04 20:22:39 +00:00
private string? ValidateAuthDescription(string description)
{
var authenticationMethods = (this.selectedAuthenticationMethods as HashSet<Auth>)!;
if(authenticationMethods.Any(n => n == Auth.NONE) && authenticationMethods.Count > 1 && string.IsNullOrWhiteSpace(this.authDescription))
return "Please describe how the selected authentication methods should be used. Especially, explain for what data the NONE method (public access) is used.";
if(authenticationMethods.Count > 1 && string.IsNullOrWhiteSpace(this.authDescription))
return "Please describe how the selected authentication methods should be used.";
return null;
}
private string GetMultiSelectionAuthText(List<Auth> selectedValues)
{
if(selectedValues.Count == 0)
return "Please select at least one authentication method";
if(selectedValues.Count == 1)
return $"You have selected 1 authentication method";
return $"You have selected {selectedValues.Count} authentication methods";
}
private string? ValidateRetrievalDescription(string description)
{
if(string.IsNullOrWhiteSpace(description))
2024-12-14 18:48:47 +00:00
return "Please describe how the data retrieval process should work. This is important for the integration of the data source into AI Studio by means of the ERI.";
return null;
}
private bool NeedHostnamePort()
{
switch (this.selectedDataSource)
{
case DataSources.NONE:
case DataSources.FILE_SYSTEM:
return false;
default:
return true;
}
}
2024-12-25 21:50:04 +00:00
private async Task AddEmbedding()
{
var dialogParameters = new DialogParameters<EmbeddingMethodDialog>
{
{ x => x.IsEditing, false },
};
var dialogReference = await this.DialogService.ShowAsync<EmbeddingMethodDialog>("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<EmbeddingMethodDialog>
{
{ 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<EmbeddingMethodDialog>("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<ConfirmDialog>("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()
{
2024-12-19 10:51:38 +00:00
await this.AutoSave();
await this.form!.Validate();
if (!this.inputIsValid)
return;
2024-12-09 18:25:22 +00:00
2024-12-14 18:48:47 +00:00
var ediSpecification = await this.selectedERIVersion.ReadSpecification(this.HttpClient);
2024-12-09 18:25:22 +00:00
if (string.IsNullOrWhiteSpace(ediSpecification))
{
// TODO: Show an error message
}
}
2024-12-04 12:49:31 +00:00
}