Implemented settings for setting up self-hosted or local provider

This commit is contained in:
Thorsten Sommer 2024-07-03 14:28:51 +02:00
parent 496ae94c2d
commit 88e3e46334
Signed by: tsommer
GPG Key ID: 371BBA77A02C0108
6 changed files with 81 additions and 9 deletions

View File

@ -101,7 +101,7 @@ public partial class Chat : ComponentBase
// Use the selected provider to get the AI response.
// By awaiting this line, we wait for the entire
// content to be streamed.
await aiText.CreateFromProviderAsync(this.selectedProvider.UsedProvider.CreateProvider(this.selectedProvider.InstanceName), this.JsRuntime, this.SettingsManager, this.selectedProvider.Model, this.chatThread);
await aiText.CreateFromProviderAsync(this.selectedProvider.UsedProvider.CreateProvider(this.selectedProvider.InstanceName, this.selectedProvider.Hostname), this.JsRuntime, this.SettingsManager, this.selectedProvider.Model, this.chatThread);
// Disable the stream state:
this.isStreaming = false;

View File

@ -25,7 +25,12 @@
<MudTd>@context.Num</MudTd>
<MudTd>@context.InstanceName</MudTd>
<MudTd>@context.UsedProvider</MudTd>
<MudTd>@context.Model</MudTd>
<MudTd>
@if(context.UsedProvider is not Providers.SELF_HOSTED)
@context.Model
else
@("as selected by provider")
</MudTd>
<MudTd Style="text-align: left;">
<MudButton Variant="Variant.Filled" Color="Color.Info" StartIcon="@Icons.Material.Filled.OpenInBrowser" Class="ma-2" Href="@this.GetProviderDashboardURL(context.UsedProvider)" Target="_blank" Disabled="@(context.UsedProvider is Providers.NONE or Providers.SELF_HOSTED)">
Open Dashboard

View File

@ -50,6 +50,8 @@ public partial class Settings : ComponentBase
{ x => x.DataInstanceName, provider.InstanceName },
{ x => x.DataProvider, provider.UsedProvider },
{ x => x.DataModel, provider.Model },
{ x => x.DataHostname, provider.Hostname },
{ x => x.IsSelfHosted, provider.IsSelfHosted },
{ x => x.IsEditing, true },
};

View File

@ -1,6 +1,7 @@
using AIStudio.Provider.Anthropic;
using AIStudio.Provider.Mistral;
using AIStudio.Provider.OpenAI;
using AIStudio.Provider.SelfHosted;
namespace AIStudio.Provider;
@ -10,9 +11,12 @@ namespace AIStudio.Provider;
public enum Providers
{
NONE,
OPEN_AI,
ANTHROPIC,
MISTRAL,
SELF_HOSTED,
}
/// <summary>
@ -27,11 +31,14 @@ public static class ExtensionsProvider
/// <returns>The human-readable name of the provider.</returns>
public static string ToName(this Providers provider) => provider switch
{
Providers.NONE => "No provider selected",
Providers.OPEN_AI => "OpenAI",
Providers.ANTHROPIC => "Anthropic",
Providers.MISTRAL => "Mistral",
Providers.NONE => "No provider selected",
Providers.SELF_HOSTED => "Self-hosted",
_ => "Unknown",
};
@ -40,13 +47,16 @@ public static class ExtensionsProvider
/// </summary>
/// <param name="provider">The provider value.</param>
/// <param name="instanceName">The used instance name.</param>
/// <param name="hostname">The hostname of the provider.</param>
/// <returns>The provider instance.</returns>
public static IProvider CreateProvider(this Providers provider, string instanceName) => provider switch
public static IProvider CreateProvider(this Providers provider, string instanceName, string hostname = "http://localhost:1234") => provider switch
{
Providers.OPEN_AI => new ProviderOpenAI { InstanceName = instanceName },
Providers.ANTHROPIC => new ProviderAnthropic { InstanceName = instanceName },
Providers.MISTRAL => new ProviderMistral { InstanceName = instanceName },
Providers.SELF_HOSTED => new ProviderSelfHosted(hostname) { InstanceName = instanceName },
_ => new NoProvider(),
};
}

View File

@ -12,7 +12,7 @@
<MudSelectItem Value="@provider">@provider</MudSelectItem>
}
</MudSelect>
<MudButton Disabled="@(this.DataProvider is Providers.NONE or Providers.SELF_HOSTED)" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@this.GetProviderCreationURL()" Target="_blank">Create account</MudButton>
<MudButton Disabled="@this.IsSelfHostedOrNone" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.OpenInBrowser" Href="@this.GetProviderCreationURL()" Target="_blank">Create account</MudButton>
</MudStack>
@* ReSharper disable once CSharpWarnings::CS8974 *@
@ -20,6 +20,7 @@
T="string"
@bind-Text="@this.dataAPIKey"
Label="API Key"
Disabled="@this.IsSelfHostedOrNone"
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.VpnKey"
@ -28,9 +29,21 @@
Validation="@this.ValidatingAPIKey"
/>
<MudTextField
T="string"
@bind-Text="@this.DataHostname"
Label="Hostname"
Disabled="@this.IsCloudProvider"
Class="mb-3"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Dns"
AdornmentColor="Color.Info"
Validation="@this.ValidatingHostname"
/>
<MudStack Row="@true" AlignItems="AlignItems.Center">
<MudButton Disabled="@(!this.CanLoadModels)" Variant="Variant.Filled" Size="Size.Small" StartIcon="@Icons.Material.Filled.Refresh" OnClick="this.ReloadModels">Load</MudButton>
<MudSelect @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.IsSelfHostedOrNone" @bind-Value="@this.DataModel" Label="Model" Class="mb-3" OpenIcon="@Icons.Material.Filled.FaceRetouchingNatural" AdornmentColor="Color.Info" Adornment="Adornment.Start" Validation="@this.ValidatingModel">
@foreach (var model in this.availableModels)
{
<MudSelectItem Value="@model">@model</MudSelectItem>

View File

@ -32,6 +32,18 @@ public partial class ProviderDialog : ComponentBase
[Parameter]
public string DataInstanceName { get; set; } = string.Empty;
/// <summary>
/// The chosen hostname for self-hosted providers.
/// </summary>
[Parameter]
public string DataHostname { get; set; } = string.Empty;
/// <summary>
/// Is this provider self-hosted?
/// </summary>
[Parameter]
public bool IsSelfHosted { get; set; }
/// <summary>
/// The provider to use.
/// </summary>
@ -99,7 +111,8 @@ public partial class ProviderDialog : ComponentBase
this.dataAPIKey = requestedSecret.Secret;
// Now, we try to load the list of available models:
await this.ReloadModels();
if(this.DataProvider is not Providers.SELF_HOSTED)
await this.ReloadModels();
}
else
{
@ -142,6 +155,8 @@ public partial class ProviderDialog : ComponentBase
InstanceName = this.DataInstanceName,
UsedProvider = this.DataProvider,
Model = this.DataModel,
IsSelfHosted = this.DataProvider is Providers.SELF_HOSTED,
Hostname = this.DataHostname,
};
// We need to instantiate the provider to store the API key:
@ -169,6 +184,9 @@ public partial class ProviderDialog : ComponentBase
private string? ValidatingModel(Model model)
{
if(this.DataProvider is Providers.SELF_HOSTED)
return null;
if (model == default)
return "Please select a model.";
@ -206,6 +224,9 @@ public partial class ProviderDialog : ComponentBase
private string? ValidatingAPIKey(string apiKey)
{
if(this.DataProvider is Providers.SELF_HOSTED)
return null;
if(!string.IsNullOrWhiteSpace(this.dataAPIKeyStorageIssue))
return this.dataAPIKeyStorageIssue;
@ -215,9 +236,24 @@ public partial class ProviderDialog : ComponentBase
return null;
}
private void Cancel() => this.MudDialog.Cancel();
private string? ValidatingHostname(string hostname)
{
if(this.DataProvider != Providers.SELF_HOSTED)
return null;
private bool CanLoadModels => !string.IsNullOrWhiteSpace(this.dataAPIKey) && this.DataProvider != Providers.NONE;
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 async Task ReloadModels()
{
@ -234,6 +270,12 @@ public partial class ProviderDialog : ComponentBase
this.availableModels.AddRange(orderedModels);
}
private bool CanLoadModels => !string.IsNullOrWhiteSpace(this.dataAPIKey) && this.DataProvider != Providers.NONE && this.DataProvider != Providers.SELF_HOSTED;
private bool IsCloudProvider => this.DataProvider is not Providers.SELF_HOSTED or Providers.NONE;
private bool IsSelfHostedOrNone => this.DataProvider is Providers.SELF_HOSTED or Providers.NONE;
private string GetProviderCreationURL() => this.DataProvider switch
{
Providers.OPEN_AI => "https://platform.openai.com/signup",