Allow selection of self-hosted models

This commit is contained in:
Thorsten Sommer 2024-07-16 10:14:22 +02:00
parent 8af8426f5d
commit 52caf96cf4
No known key found for this signature in database
GPG Key ID: B0B7E2FC074BF1F5
4 changed files with 89 additions and 29 deletions

View File

@ -1,5 +1,6 @@
@page "/settings"
@using AIStudio.Provider
@using Host = AIStudio.Provider.SelfHosted.Host
<MudText Typo="Typo.h3" Class="mb-12">Settings</MudText>
@ -26,10 +27,18 @@
<MudTd>@context.InstanceName</MudTd>
<MudTd>@context.UsedProvider</MudTd>
<MudTd>
@if(context.UsedProvider is not Providers.SELF_HOSTED)
@if (context.UsedProvider is not Providers.SELF_HOSTED)
{
@context.Model
}
else if (context.UsedProvider is Providers.SELF_HOSTED && context.Host is not Host.LLAMACPP)
{
@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)">

View File

@ -33,7 +33,7 @@ public sealed class ProviderSelfHosted(Settings.Provider provider) : BaseProvide
// Prepare the OpenAI HTTP chat request:
var providerChatRequest = JsonSerializer.Serialize(new ChatRequest
{
Model = (await this.GetTextModels(jsRuntime, settings, token: token)).First().Id,
Model = chatModel.Id,
// Build the messages:
// - First of all the system prompt
@ -137,17 +137,33 @@ public sealed class ProviderSelfHosted(Settings.Provider provider) : BaseProvide
public async Task<IEnumerable<Provider.Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
{
var request = new HttpRequestMessage(HttpMethod.Get, "models");
var response = await this.httpClient.SendAsync(request, token);
if(!response.IsSuccessStatusCode)
return [];
try
{
switch (provider.Host)
{
case Host.LLAMACPP:
// Right now, llama.cpp only supports one model.
// There is no API to list the model(s).
return [ new Provider.Model("as configured by llama.cpp") ];
case Host.LM_STUDIO:
case Host.OLLAMA:
var lmStudioRequest = new HttpRequestMessage(HttpMethod.Get, "models");
var lmStudioResponse = await this.httpClient.SendAsync(lmStudioRequest, token);
if(!lmStudioResponse.IsSuccessStatusCode)
return [];
var modelResponse = await response.Content.ReadFromJsonAsync<ModelsResponse>(token);
if (modelResponse.Data.Length > 1)
Console.WriteLine("Warning: multiple models found; using the first one.");
var firstModel = modelResponse.Data.First();
return [ new Provider.Model(firstModel.Id) ];
var lmStudioModelResponse = await lmStudioResponse.Content.ReadFromJsonAsync<ModelsResponse>(token);
return lmStudioModelResponse.Data.Select(n => new Provider.Model(n.Id));
}
return [];
}
catch(Exception e)
{
Console.WriteLine($"Failed to load text models from self-hosted provider: {e.Message}");
return [];
}
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously

View File

@ -51,8 +51,8 @@
</MudSelect>
<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 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">
<MudButton Disabled="@(!this.CanLoadModels())" 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">
@foreach (var model in this.availableModels)
{
<MudSelectItem Value="@model">@model</MudSelectItem>

View File

@ -122,10 +122,11 @@ public partial class ProviderDialog : ComponentBase
this.dataEditingPreviousInstanceName = this.DataInstanceName.ToLowerInvariant();
//
// We cannot load the API key nor models for self-hosted providers:
// We cannot load the API key for self-hosted providers:
//
if (this.DataProvider is Providers.SELF_HOSTED)
{
await this.ReloadModels();
await base.OnInitializedAsync();
return;
}
@ -182,19 +183,21 @@ public partial class ProviderDialog : ComponentBase
// Use the data model to store the provider.
// We just return this data to the parent component:
var addedProviderSettings = this.CreateProviderSettings();
// We need to instantiate the provider to store the API key:
var provider = addedProviderSettings.CreateProvider();
// Store the API key in the OS secure storage:
var storeResponse = await this.SettingsManager.SetAPIKey(this.JsRuntime, provider, this.dataAPIKey);
if (!storeResponse.Success)
if (addedProviderSettings.UsedProvider != Providers.SELF_HOSTED)
{
this.dataAPIKeyStorageIssue = $"Failed to store the API key in the operating system. The message was: {storeResponse.Issue}. Please try again.";
await this.form.Validate();
return;
// We need to instantiate the provider to store the API key:
var provider = addedProviderSettings.CreateProvider();
// Store the API key in the OS secure storage:
var storeResponse = await this.SettingsManager.SetAPIKey(this.JsRuntime, provider, this.dataAPIKey);
if (!storeResponse.Success)
{
this.dataAPIKeyStorageIssue = $"Failed to store the API key in the operating system. The message was: {storeResponse.Issue}. Please try again.";
await this.form.Validate();
return;
}
}
this.MudDialog.Close(DialogResult.Ok(addedProviderSettings));
}
@ -219,7 +222,7 @@ public partial class ProviderDialog : ComponentBase
private string? ValidatingModel(Model model)
{
if(this.DataProvider is Providers.SELF_HOSTED)
if(this.DataProvider is Providers.SELF_HOSTED && this.DataHost == Host.LLAMACPP)
return null;
if (model == default)
@ -319,11 +322,43 @@ 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 CanLoadModels()
{
if (this.DataProvider is Providers.SELF_HOSTED)
{
switch (this.DataHost)
{
case Host.NONE:
return false;
case Host.LLAMACPP:
return false;
case Host.LM_STUDIO:
return true;
case Host.OLLAMA:
return true;
default:
return false;
}
}
if(this.DataProvider is Providers.NONE)
return false;
if(string.IsNullOrWhiteSpace(this.dataAPIKey))
return false;
return true;
}
private bool IsCloudProvider => this.DataProvider is not Providers.SELF_HOSTED;
private bool IsSelfHostedOrNone => this.DataProvider is Providers.SELF_HOSTED or Providers.NONE;
private bool IsNoneProvider => this.DataProvider is Providers.NONE;
private string GetProviderCreationURL() => this.DataProvider switch
{