Refactored provider validation

This commit is contained in:
Thorsten Sommer 2024-12-02 20:55:01 +01:00
parent d65e9c5786
commit e0b69ee655
Signed by: tsommer
GPG Key ID: 371BBA77A02C0108
3 changed files with 116 additions and 83 deletions

View File

@ -6,7 +6,7 @@
<MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues"> <MudForm @ref="@this.form" @bind-IsValid="@this.dataIsValid" @bind-Errors="@this.dataIssues">
<MudStack Row="@true" AlignItems="AlignItems.Center"> <MudStack Row="@true" AlignItems="AlignItems.Center">
@* ReSharper disable once CSharpWarnings::CS8974 *@ @* ReSharper disable once CSharpWarnings::CS8974 *@
<MudSelect @bind-Value="@this.DataLLMProvider" Label="Provider" Class="mb-3" OpenIcon="@Icons.Material.Filled.AccountBalance" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.ValidatingProvider"> <MudSelect @bind-Value="@this.DataLLMProvider" Label="Provider" Class="mb-3" OpenIcon="@Icons.Material.Filled.AccountBalance" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingProvider">
@foreach (LLMProviders provider in Enum.GetValues(typeof(LLMProviders))) @foreach (LLMProviders provider in Enum.GetValues(typeof(LLMProviders)))
{ {
<MudSelectItem Value="@provider">@provider</MudSelectItem> <MudSelectItem Value="@provider">@provider</MudSelectItem>
@ -26,7 +26,7 @@
AdornmentIcon="@Icons.Material.Filled.VpnKey" AdornmentIcon="@Icons.Material.Filled.VpnKey"
AdornmentColor="Color.Info" AdornmentColor="Color.Info"
InputType="InputType.Password" InputType="InputType.Password"
Validation="@this.ValidatingAPIKey" Validation="@this.providerValidation.ValidatingAPIKey"
/> />
<MudTextField <MudTextField
@ -38,11 +38,11 @@
Adornment="Adornment.Start" Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Dns" AdornmentIcon="@Icons.Material.Filled.Dns"
AdornmentColor="Color.Info" AdornmentColor="Color.Info"
Validation="@this.ValidatingHostname" Validation="@this.providerValidation.ValidatingHostname"
UserAttributes="@SPELLCHECK_ATTRIBUTES" UserAttributes="@SPELLCHECK_ATTRIBUTES"
/> />
<MudSelect Disabled="@(!this.DataLLMProvider.IsHostNeeded())" @bind-Value="@this.DataHost" Label="Host" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.ValidatingHost"> <MudSelect Disabled="@(!this.DataLLMProvider.IsHostNeeded())" @bind-Value="@this.DataHost" Label="Host" Class="mb-3" OpenIcon="@Icons.Material.Filled.ExpandMore" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingHost">
@foreach (Host host in Enum.GetValues(typeof(Host))) @foreach (Host host in Enum.GetValues(typeof(Host)))
{ {
<MudSelectItem Value="@host">@host.Name()</MudSelectItem> <MudSelectItem Value="@host">@host.Name()</MudSelectItem>
@ -68,7 +68,7 @@
else else
{ {
<MudButton Disabled="@(!this.DataLLMProvider.CanLoadModels(this.DataHost, this.dataAPIKey))" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.Refresh" OnClick="this.ReloadModels">Load</MudButton> <MudButton Disabled="@(!this.DataLLMProvider.CanLoadModels(this.DataHost, this.dataAPIKey))" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.Refresh" OnClick="this.ReloadModels">Load</MudButton>
<MudSelect Disabled="@this.IsNoneProvider" @bind-Value="@this.DataModel" Label="Model" Class="mb-3" OpenIcon="@Icons.Material.Filled.FaceRetouchingNatural" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.ValidatingModel"> <MudSelect Disabled="@this.IsNoneProvider" @bind-Value="@this.DataModel" Label="Model" Class="mb-3" OpenIcon="@Icons.Material.Filled.FaceRetouchingNatural" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.providerValidation.ValidatingModel">
@foreach (var model in this.availableModels) @foreach (var model in this.availableModels)
{ {
<MudSelectItem Value="@model">@model</MudSelectItem> <MudSelectItem Value="@model">@model</MudSelectItem>
@ -89,7 +89,7 @@
Adornment="Adornment.Start" Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Lightbulb" AdornmentIcon="@Icons.Material.Filled.Lightbulb"
AdornmentColor="Color.Info" AdornmentColor="Color.Info"
Validation="@this.ValidatingInstanceName" Validation="@this.providerValidation.ValidatingInstanceName"
UserAttributes="@SPELLCHECK_ATTRIBUTES" UserAttributes="@SPELLCHECK_ATTRIBUTES"
/> />

View File

@ -1,5 +1,6 @@
using AIStudio.Provider; using AIStudio.Provider;
using AIStudio.Settings; using AIStudio.Settings;
using AIStudio.Tools.Validation;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
@ -98,6 +99,19 @@ public partial class ProviderDialog : ComponentBase, ISecretId
private readonly List<Model> availableModels = new(); private readonly List<Model> availableModels = new();
private readonly Encryption encryption = Program.ENCRYPTION; private readonly Encryption encryption = Program.ENCRYPTION;
private readonly ProviderValidation providerValidation;
public ProviderDialog()
{
this.providerValidation = new()
{
GetProvider = () => this.DataLLMProvider,
GetAPIKeyStorageIssue = () => this.dataAPIKeyStorageIssue,
GetPreviousInstanceName = () => this.dataEditingPreviousInstanceName,
GetUsedInstanceNames = () => this.UsedInstanceNames,
GetHost = () => this.DataHost,
};
}
private Settings.Provider CreateProviderSettings() private Settings.Provider CreateProviderSettings()
{ {
@ -212,25 +226,6 @@ public partial class ProviderDialog : ComponentBase, ISecretId
this.MudDialog.Close(DialogResult.Ok(addedProviderSettings)); this.MudDialog.Close(DialogResult.Ok(addedProviderSettings));
} }
private string? ValidatingProvider(LLMProviders llmProvider)
{
if (llmProvider == LLMProviders.NONE)
return "Please select a provider.";
return null;
}
private string? ValidatingHost(Host host)
{
if(this.DataLLMProvider is not LLMProviders.SELF_HOSTED)
return null;
if (host == Host.NONE)
return "Please select a host.";
return null;
}
private string? ValidateManuallyModel(string manuallyModel) private string? ValidateManuallyModel(string manuallyModel)
{ {
if (this.DataLLMProvider is LLMProviders.FIREWORKS && string.IsNullOrWhiteSpace(manuallyModel)) if (this.DataLLMProvider is LLMProviders.FIREWORKS && string.IsNullOrWhiteSpace(manuallyModel))
@ -238,64 +233,6 @@ public partial class ProviderDialog : ComponentBase, ISecretId
return null; return null;
} }
private string? ValidatingModel(Model model)
{
if(this.DataLLMProvider is LLMProviders.SELF_HOSTED && this.DataHost == Host.LLAMACPP)
return null;
if (model == default)
return "Please select a model.";
return null;
}
private string? ValidatingInstanceName(string instanceName)
{
if (string.IsNullOrWhiteSpace(instanceName))
return "Please enter an instance name.";
if (instanceName.Length > 40)
return "The instance name must not exceed 40 characters.";
// The instance name must be unique:
var lowerInstanceName = instanceName.ToLowerInvariant();
if (lowerInstanceName != this.dataEditingPreviousInstanceName && this.UsedInstanceNames.Contains(lowerInstanceName))
return "The instance name must be unique; the chosen name is already in use.";
return null;
}
private string? ValidatingAPIKey(string apiKey)
{
if(this.DataLLMProvider is LLMProviders.SELF_HOSTED)
return null;
if(!string.IsNullOrWhiteSpace(this.dataAPIKeyStorageIssue))
return this.dataAPIKeyStorageIssue;
if(string.IsNullOrWhiteSpace(apiKey))
return "Please enter an API key.";
return null;
}
private string? ValidatingHostname(string hostname)
{
if(this.DataLLMProvider != LLMProviders.SELF_HOSTED)
return null;
if(string.IsNullOrWhiteSpace(hostname))
return "Please enter a hostname, e.g., http://localhost:1234";
if(!hostname.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) && !hostname.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
return "The hostname must start with either http:// or https://";
if(!Uri.TryCreate(hostname, UriKind.Absolute, out _))
return "The hostname is not a valid HTTP(S) URL.";
return null;
}
private void Cancel() => this.MudDialog.Cancel(); private void Cancel() => this.MudDialog.Cancel();

View File

@ -0,0 +1,96 @@
using AIStudio.Provider;
using Host = AIStudio.Provider.SelfHosted.Host;
namespace AIStudio.Tools.Validation;
public sealed class ProviderValidation
{
public Func<LLMProviders> GetProvider { get; init; } = () => LLMProviders.NONE;
public Func<string> GetAPIKeyStorageIssue { get; init; } = () => string.Empty;
public Func<string> GetPreviousInstanceName { get; init; } = () => string.Empty;
public Func<IEnumerable<string>> GetUsedInstanceNames { get; init; } = () => [];
public Func<Host> GetHost { get; init; } = () => Host.NONE;
public string? ValidatingHostname(string hostname)
{
if(this.GetProvider() != LLMProviders.SELF_HOSTED)
return null;
if(string.IsNullOrWhiteSpace(hostname))
return "Please enter a hostname, e.g., http://localhost:1234";
if(!hostname.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) && !hostname.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
return "The hostname must start with either http:// or https://";
if(!Uri.TryCreate(hostname, UriKind.Absolute, out _))
return "The hostname is not a valid HTTP(S) URL.";
return null;
}
public string? ValidatingAPIKey(string apiKey)
{
if(this.GetProvider() is LLMProviders.SELF_HOSTED)
return null;
var apiKeyStorageIssue = this.GetAPIKeyStorageIssue();
if(!string.IsNullOrWhiteSpace(apiKeyStorageIssue))
return apiKeyStorageIssue;
if(string.IsNullOrWhiteSpace(apiKey))
return "Please enter an API key.";
return null;
}
public string? ValidatingInstanceName(string instanceName)
{
if (string.IsNullOrWhiteSpace(instanceName))
return "Please enter an instance name.";
if (instanceName.Length > 40)
return "The instance name must not exceed 40 characters.";
// The instance name must be unique:
var lowerInstanceName = instanceName.ToLowerInvariant();
if (lowerInstanceName != this.GetPreviousInstanceName() && this.GetUsedInstanceNames().Contains(lowerInstanceName))
return "The instance name must be unique; the chosen name is already in use.";
return null;
}
public string? ValidatingModel(Model model)
{
if(this.GetProvider() is LLMProviders.SELF_HOSTED && this.GetHost() == Host.LLAMACPP)
return null;
if (model == default)
return "Please select a model.";
return null;
}
public string? ValidatingProvider(LLMProviders llmProvider)
{
if (llmProvider == LLMProviders.NONE)
return "Please select a provider.";
return null;
}
public string? ValidatingHost(Host host)
{
if(this.GetProvider() is not LLMProviders.SELF_HOSTED)
return null;
if (host == Host.NONE)
return "Please select a host.";
return null;
}
}