mirror of
				https://github.com/MindWorkAI/AI-Studio.git
				synced 2025-10-31 01:00:20 +00:00 
			
		
		
		
	Allow selection of self-hosted models
This commit is contained in:
		
							parent
							
								
									8af8426f5d
								
							
						
					
					
						commit
						52caf96cf4
					
				| @ -1,5 +1,6 @@ | |||||||
| @page "/settings" | @page "/settings" | ||||||
| @using AIStudio.Provider | @using AIStudio.Provider | ||||||
|  | @using Host = AIStudio.Provider.SelfHosted.Host | ||||||
| 
 | 
 | ||||||
| <MudText Typo="Typo.h3" Class="mb-12">Settings</MudText> | <MudText Typo="Typo.h3" Class="mb-12">Settings</MudText> | ||||||
| 
 | 
 | ||||||
| @ -26,10 +27,18 @@ | |||||||
|                 <MudTd>@context.InstanceName</MudTd> |                 <MudTd>@context.InstanceName</MudTd> | ||||||
|                 <MudTd>@context.UsedProvider</MudTd> |                 <MudTd>@context.UsedProvider</MudTd> | ||||||
|                 <MudTd> |                 <MudTd> | ||||||
|                     @if(context.UsedProvider is not Providers.SELF_HOSTED) |                     @if (context.UsedProvider is not Providers.SELF_HOSTED) | ||||||
|  |                     { | ||||||
|                         @context.Model |                         @context.Model | ||||||
|  |                     } | ||||||
|  |                     else if (context.UsedProvider is Providers.SELF_HOSTED && context.Host is not Host.LLAMACPP) | ||||||
|  |                     { | ||||||
|  |                         @context.Model | ||||||
|  |                     } | ||||||
|                     else |                     else | ||||||
|  |                     { | ||||||
|                         @("as selected by provider") |                         @("as selected by provider") | ||||||
|  |                     } | ||||||
|                 </MudTd> |                 </MudTd> | ||||||
|                 <MudTd Style="text-align: left;"> |                 <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)"> |                     <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)"> | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ public sealed class ProviderSelfHosted(Settings.Provider provider) : BaseProvide | |||||||
|         // Prepare the OpenAI HTTP chat request: |         // Prepare the OpenAI HTTP chat request: | ||||||
|         var providerChatRequest = JsonSerializer.Serialize(new ChatRequest |         var providerChatRequest = JsonSerializer.Serialize(new ChatRequest | ||||||
|         { |         { | ||||||
|             Model = (await this.GetTextModels(jsRuntime, settings, token: token)).First().Id, |             Model = chatModel.Id, | ||||||
|              |              | ||||||
|             // Build the messages: |             // Build the messages: | ||||||
|             // - First of all the system prompt |             // - 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) |     public async Task<IEnumerable<Provider.Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default) | ||||||
|     { |     { | ||||||
|         var request = new HttpRequestMessage(HttpMethod.Get, "models"); |         try | ||||||
|         var response = await this.httpClient.SendAsync(request, token); |         { | ||||||
|         if(!response.IsSuccessStatusCode) |             switch (provider.Host) | ||||||
|             return []; |             { | ||||||
|  |                 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); |                     var lmStudioModelResponse = await lmStudioResponse.Content.ReadFromJsonAsync<ModelsResponse>(token); | ||||||
|         if (modelResponse.Data.Length > 1) |                     return lmStudioModelResponse.Data.Select(n => new Provider.Model(n.Id)); | ||||||
|             Console.WriteLine("Warning: multiple models found; using the first one."); |             } | ||||||
|          | 
 | ||||||
|         var firstModel = modelResponse.Data.First(); |             return []; | ||||||
|         return [ new Provider.Model(firstModel.Id) ]; |         } | ||||||
|  |         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 |     #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously | ||||||
|  | |||||||
| @ -51,8 +51,8 @@ | |||||||
|             </MudSelect> |             </MudSelect> | ||||||
| 
 | 
 | ||||||
|             <MudStack Row="@true" AlignItems="AlignItems.Center"> |             <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> |                 <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"> |                 <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) |                     @foreach (var model in this.availableModels) | ||||||
|                     { |                     { | ||||||
|                         <MudSelectItem Value="@model">@model</MudSelectItem> |                         <MudSelectItem Value="@model">@model</MudSelectItem> | ||||||
|  | |||||||
| @ -122,10 +122,11 @@ public partial class ProviderDialog : ComponentBase | |||||||
|             this.dataEditingPreviousInstanceName = this.DataInstanceName.ToLowerInvariant(); |             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) |             if (this.DataProvider is Providers.SELF_HOSTED) | ||||||
|             { |             { | ||||||
|  |                 await this.ReloadModels(); | ||||||
|                 await base.OnInitializedAsync(); |                 await base.OnInitializedAsync(); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
| @ -182,19 +183,21 @@ public partial class ProviderDialog : ComponentBase | |||||||
|         // Use the data model to store the provider. |         // Use the data model to store the provider. | ||||||
|         // We just return this data to the parent component: |         // We just return this data to the parent component: | ||||||
|         var addedProviderSettings = this.CreateProviderSettings(); |         var addedProviderSettings = this.CreateProviderSettings(); | ||||||
|          |         if (addedProviderSettings.UsedProvider != Providers.SELF_HOSTED) | ||||||
|         // 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."; |             // We need to instantiate the provider to store the API key: | ||||||
|             await this.form.Validate(); |             var provider = addedProviderSettings.CreateProvider(); | ||||||
|             return; |              | ||||||
|  |             // 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)); |         this.MudDialog.Close(DialogResult.Ok(addedProviderSettings)); | ||||||
|     } |     } | ||||||
|      |      | ||||||
| @ -219,7 +222,7 @@ public partial class ProviderDialog : ComponentBase | |||||||
|      |      | ||||||
|     private string? ValidatingModel(Model model) |     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; |             return null; | ||||||
|          |          | ||||||
|         if (model == default) |         if (model == default) | ||||||
| @ -319,11 +322,43 @@ public partial class ProviderDialog : ComponentBase | |||||||
|         this.availableModels.AddRange(orderedModels); |         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 IsCloudProvider => this.DataProvider is not Providers.SELF_HOSTED; | ||||||
|      |      | ||||||
|     private bool IsSelfHostedOrNone => this.DataProvider is Providers.SELF_HOSTED or Providers.NONE; |     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 |     private string GetProviderCreationURL() => this.DataProvider switch | ||||||
|     { |     { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user