Merge branch '63-bug-cannot-read-secret-for-new-provider' into 'main'

Resolve "Bug: Cannot read secret for new provider"

Closes #63

See merge request products/mindwork-ai-studio!16
This commit is contained in:
Thorsten 2024-06-03 17:45:53 +00:00
commit 2de2017092
4 changed files with 25 additions and 14 deletions

View File

@ -48,16 +48,18 @@ public interface IProvider
/// </summary> /// </summary>
/// <param name="jsRuntime">The JS runtime to access the Rust code.</param> /// <param name="jsRuntime">The JS runtime to access the Rust code.</param>
/// <param name="settings">The settings manager to access the API key.</param> /// <param name="settings">The settings manager to access the API key.</param>
/// <param name="apiKeyProvisional">The provisional API key to use. Useful when the user is adding a new provider. When null, the stored API key is used.</param>
/// <param name="token">The cancellation token.</param> /// <param name="token">The cancellation token.</param>
/// <returns>The list of text models.</returns> /// <returns>The list of text models.</returns>
public Task<IEnumerable<Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, CancellationToken token = default); public Task<IEnumerable<Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default);
/// <summary> /// <summary>
/// Load all possible image models that can be used with this provider. /// Load all possible image models that can be used with this provider.
/// </summary> /// </summary>
/// <param name="jsRuntime">The JS runtime to access the Rust code.</param> /// <param name="jsRuntime">The JS runtime to access the Rust code.</param>
/// <param name="settings">The settings manager to access the API key.</param> /// <param name="settings">The settings manager to access the API key.</param>
/// <param name="apiKeyProvisional">The provisional API key to use. Useful when the user is adding a new provider. When null, the stored API key is used.</param>
/// <param name="token">The cancellation token.</param> /// <param name="token">The cancellation token.</param>
/// <returns>The list of image models.</returns> /// <returns>The list of image models.</returns>
public Task<IEnumerable<Model>> GetImageModels(IJSRuntime jsRuntime, SettingsManager settings, CancellationToken token = default); public Task<IEnumerable<Model>> GetImageModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default);
} }

View File

@ -13,9 +13,9 @@ public class NoProvider : IProvider
public string InstanceName { get; set; } = "None"; public string InstanceName { get; set; } = "None";
public Task<IEnumerable<Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, CancellationToken token = default) => Task.FromResult<IEnumerable<Model>>([]); public Task<IEnumerable<Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default) => Task.FromResult<IEnumerable<Model>>([]);
public Task<IEnumerable<Model>> GetImageModels(IJSRuntime jsRuntime, SettingsManager settings, CancellationToken token = default) => Task.FromResult<IEnumerable<Model>>([]); public Task<IEnumerable<Model>> GetImageModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default) => Task.FromResult<IEnumerable<Model>>([]);
public async IAsyncEnumerable<string> StreamChatCompletion(IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread chatChatThread, [EnumeratorCancellation] CancellationToken token = default) public async IAsyncEnumerable<string> StreamChatCompletion(IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread chatChatThread, [EnumeratorCancellation] CancellationToken token = default)
{ {

View File

@ -151,27 +151,36 @@ public sealed class ProviderOpenAI() : BaseProvider("https://api.openai.com/v1/"
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
/// <inheritdoc /> /// <inheritdoc />
public Task<IEnumerable<Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, CancellationToken token = default) public Task<IEnumerable<Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
{ {
return this.LoadModels(jsRuntime, settings, "gpt-", token); return this.LoadModels(jsRuntime, settings, "gpt-", token, apiKeyProvisional);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<IEnumerable<Model>> GetImageModels(IJSRuntime jsRuntime, SettingsManager settings, CancellationToken token = default) public Task<IEnumerable<Model>> GetImageModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
{ {
return this.LoadModels(jsRuntime, settings, "dall-e-", token); return this.LoadModels(jsRuntime, settings, "dall-e-", token, apiKeyProvisional);
} }
#endregion #endregion
private async Task<IEnumerable<Model>> LoadModels(IJSRuntime jsRuntime, SettingsManager settings, string prefix, CancellationToken token) private async Task<IEnumerable<Model>> LoadModels(IJSRuntime jsRuntime, SettingsManager settings, string prefix, CancellationToken token, string? apiKeyProvisional = null)
{ {
var requestedSecret = await settings.GetAPIKey(jsRuntime, this); var secretKey = apiKeyProvisional switch
if (!requestedSecret.Success) {
not null => apiKeyProvisional,
_ => await settings.GetAPIKey(jsRuntime, this) switch
{
{ Success: true } result => result.Secret,
_ => null,
}
};
if (secretKey is null)
return []; return [];
var request = new HttpRequestMessage(HttpMethod.Get, "models"); var request = new HttpRequestMessage(HttpMethod.Get, "models");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", requestedSecret.Secret); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secretKey);
var response = await this.httpClient.SendAsync(request, token); var response = await this.httpClient.SendAsync(request, token);
if(!response.IsSuccessStatusCode) if(!response.IsSuccessStatusCode)

View File

@ -225,7 +225,7 @@ public partial class ProviderDialog : ComponentBase
if(provider is NoProvider) if(provider is NoProvider)
return; return;
var models = await provider.GetTextModels(this.JsRuntime, this.SettingsManager); var models = await provider.GetTextModels(this.JsRuntime, this.SettingsManager, this.dataAPIKey);
// Order descending by ID means that the newest models probably come first: // Order descending by ID means that the newest models probably come first:
var orderedModels = models.OrderByDescending(n => n.Id); var orderedModels = models.OrderByDescending(n => n.Id);