mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-04-28 21:59:48 +00:00
Migrated the deletion of secrets from Tauri JS to the runtime API
This commit is contained in:
parent
54c2cd3740
commit
88ea640a68
@ -2,24 +2,18 @@ using AIStudio.Chat;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
|
||||
using RustService = AIStudio.Tools.RustService;
|
||||
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
|
||||
namespace AIStudio.Agents;
|
||||
|
||||
public abstract class AgentBase(ILogger<AgentBase> logger, RustService rustService, SettingsManager settingsManager, IJSRuntime jsRuntime, ThreadSafeRandom rng) : IAgent
|
||||
public abstract class AgentBase(ILogger<AgentBase> logger, SettingsManager settingsManager, ThreadSafeRandom rng) : IAgent
|
||||
{
|
||||
protected SettingsManager SettingsManager { get; init; } = settingsManager;
|
||||
|
||||
protected IJSRuntime JsRuntime { get; init; } = jsRuntime;
|
||||
|
||||
protected ThreadSafeRandom RNG { get; init; } = rng;
|
||||
|
||||
protected ILogger<AgentBase> Logger { get; init; } = logger;
|
||||
|
||||
protected RustService RustService { get; init; } = rustService;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the type or category of this agent.
|
||||
/// </summary>
|
||||
@ -109,6 +103,6 @@ public abstract class AgentBase(ILogger<AgentBase> logger, RustService rustServi
|
||||
// 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(providerSettings.CreateProvider(this.Logger, this.RustService), this.JsRuntime, this.SettingsManager, providerSettings.Model, thread);
|
||||
await aiText.CreateFromProviderAsync(providerSettings.CreateProvider(this.Logger), this.SettingsManager, providerSettings.Model, thread);
|
||||
}
|
||||
}
|
@ -1,11 +1,9 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Settings;
|
||||
|
||||
using RustService = AIStudio.Tools.RustService;
|
||||
|
||||
namespace AIStudio.Agents;
|
||||
|
||||
public sealed class AgentTextContentCleaner(ILogger<AgentBase> logger, RustService rustService, SettingsManager settingsManager, IJSRuntime jsRuntime, ThreadSafeRandom rng) : AgentBase(logger, rustService, settingsManager, jsRuntime, rng)
|
||||
public sealed class AgentTextContentCleaner(ILogger<AgentBase> logger, SettingsManager settingsManager, ThreadSafeRandom rng) : AgentBase(logger, settingsManager, rng)
|
||||
{
|
||||
private static readonly ContentBlock EMPTY_BLOCK = new()
|
||||
{
|
||||
|
@ -155,7 +155,7 @@ public abstract partial class AssistantBase : 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.providerSettings.CreateProvider(this.Logger, this.RustService), this.JsRuntime, this.SettingsManager, this.providerSettings.Model, this.chatThread);
|
||||
await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(this.Logger), this.SettingsManager, this.providerSettings.Model, this.chatThread);
|
||||
|
||||
this.isProcessing = false;
|
||||
this.StateHasChanged();
|
||||
|
@ -42,9 +42,6 @@ public partial class ContentBlockComponent : ComponentBase
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IJSRuntime JsRuntime { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ISnackbar Snackbar { get; init; } = null!;
|
||||
|
||||
|
@ -29,7 +29,7 @@ public sealed class ContentImage : IContent
|
||||
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CreateFromProviderAsync(IProvider provider, IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread chatChatThread, CancellationToken token = default)
|
||||
public Task CreateFromProviderAsync(IProvider provider, SettingsManager settings, Model chatModel, ChatThread chatChatThread, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -35,18 +35,18 @@ public sealed class ContentText : IContent
|
||||
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task CreateFromProviderAsync(IProvider provider, IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread? chatThread, CancellationToken token = default)
|
||||
public async Task CreateFromProviderAsync(IProvider provider, SettingsManager settings, Model chatModel, ChatThread? chatThread, CancellationToken token = default)
|
||||
{
|
||||
if(chatThread is null)
|
||||
return;
|
||||
|
||||
// Store the last time we got a response. We use this later,
|
||||
// Store the last time we got a response. We use this ater
|
||||
// to determine whether we should notify the UI about the
|
||||
// new content or not. Depends on the energy saving mode
|
||||
// the user chose.
|
||||
var last = DateTimeOffset.Now;
|
||||
|
||||
// Start another thread by using a task, to uncouple
|
||||
// Start another thread by using a task to uncouple
|
||||
// the UI thread from the AI processing:
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
@ -54,7 +54,7 @@ public sealed class ContentText : IContent
|
||||
this.InitialRemoteWait = true;
|
||||
|
||||
// Iterate over the responses from the AI:
|
||||
await foreach (var deltaText in provider.StreamChatCompletion(jsRuntime, settings, chatModel, chatThread, token))
|
||||
await foreach (var deltaText in provider.StreamChatCompletion(chatModel, chatThread, token))
|
||||
{
|
||||
// When the user cancels the request, we stop the loop:
|
||||
if (token.IsCancellationRequested)
|
||||
@ -89,7 +89,7 @@ public sealed class ContentText : IContent
|
||||
}
|
||||
|
||||
// Stop the waiting animation (in case the loop
|
||||
// was stopped or no content was received):
|
||||
// was stopped, or no content was received):
|
||||
this.InitialRemoteWait = false;
|
||||
this.IsStreaming = false;
|
||||
}, token);
|
||||
|
@ -42,5 +42,5 @@ public interface IContent
|
||||
/// <summary>
|
||||
/// Uses the provider to create the content.
|
||||
/// </summary>
|
||||
public Task CreateFromProviderAsync(IProvider provider, IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread chatChatThread, CancellationToken token = default);
|
||||
public Task CreateFromProviderAsync(IProvider provider, SettingsManager settings, Model chatModel, ChatThread chatChatThread, CancellationToken token = default);
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
@using AIStudio.Components
|
||||
@using AIStudio.Provider
|
||||
@using AIStudio.Provider.SelfHosted
|
||||
@using MudBlazor
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
|
@ -75,9 +75,6 @@ public partial class ProviderDialog : ComponentBase
|
||||
[Inject]
|
||||
private SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IJSRuntime JsRuntime { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ILogger<ProviderDialog> Logger { get; init; } = null!;
|
||||
|
||||
@ -142,7 +139,7 @@ public partial class ProviderDialog : ComponentBase
|
||||
}
|
||||
|
||||
var loadedProviderSettings = this.CreateProviderSettings();
|
||||
var provider = loadedProviderSettings.CreateProvider(this.Logger, this.RustService);
|
||||
var provider = loadedProviderSettings.CreateProvider(this.Logger);
|
||||
if(provider is NoProvider)
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
@ -196,7 +193,7 @@ public partial class ProviderDialog : ComponentBase
|
||||
if (addedProviderSettings.UsedProvider != Providers.SELF_HOSTED)
|
||||
{
|
||||
// We need to instantiate the provider to store the API key:
|
||||
var provider = addedProviderSettings.CreateProvider(this.Logger, this.RustService);
|
||||
var provider = addedProviderSettings.CreateProvider(this.Logger);
|
||||
|
||||
// Store the API key in the OS secure storage:
|
||||
var storeResponse = await this.RustService.SetAPIKey(provider, this.dataAPIKey);
|
||||
@ -327,11 +324,11 @@ public partial class ProviderDialog : ComponentBase
|
||||
private async Task ReloadModels()
|
||||
{
|
||||
var currentProviderSettings = this.CreateProviderSettings();
|
||||
var provider = currentProviderSettings.CreateProvider(this.Logger, this.RustService);
|
||||
var provider = currentProviderSettings.CreateProvider(this.Logger);
|
||||
if(provider is NoProvider)
|
||||
return;
|
||||
|
||||
var models = await provider.GetTextModels(this.JsRuntime, this.SettingsManager, this.dataAPIKey);
|
||||
var models = await provider.GetTextModels(this.dataAPIKey);
|
||||
|
||||
// Order descending by ID means that the newest models probably come first:
|
||||
var orderedModels = models.OrderByDescending(n => n.Id);
|
||||
|
@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
using RustService = AIStudio.Tools.RustService;
|
||||
|
||||
namespace AIStudio.Pages;
|
||||
|
||||
@ -21,9 +20,6 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
||||
[Inject]
|
||||
private SettingsManager SettingsManager { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
public IJSRuntime JsRuntime { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ThreadSafeRandom RNG { get; init; } = null!;
|
||||
|
||||
@ -33,9 +29,6 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
||||
[Inject]
|
||||
private ILogger<Chat> Logger { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
private InnerScrolling scrollingArea = null!;
|
||||
|
||||
private const Placement TOOLBAR_TOOLTIP_PLACEMENT = Placement.Bottom;
|
||||
@ -195,7 +188,7 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
|
||||
// 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.providerSettings.CreateProvider(this.Logger, this.RustService), this.JsRuntime, this.SettingsManager, this.providerSettings.Model, this.chatThread);
|
||||
await aiText.CreateFromProviderAsync(this.providerSettings.CreateProvider(this.Logger), this.SettingsManager, this.providerSettings.Model, this.chatThread);
|
||||
|
||||
// Save the chat:
|
||||
if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
|
||||
|
@ -19,9 +19,6 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IJSRuntime JsRuntime { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private MessageBus MessageBus { get; init; } = null!;
|
||||
|
||||
@ -117,8 +114,8 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
|
||||
if (dialogResult is null || dialogResult.Canceled)
|
||||
return;
|
||||
|
||||
var providerInstance = provider.CreateProvider(this.Logger, this.RustService);
|
||||
var deleteSecretResponse = await this.SettingsManager.DeleteAPIKey(this.JsRuntime, providerInstance);
|
||||
var providerInstance = provider.CreateProvider(this.Logger);
|
||||
var deleteSecretResponse = await this.RustService.DeleteAPIKey(providerInstance);
|
||||
if(deleteSecretResponse.Success)
|
||||
{
|
||||
this.SettingsManager.ConfigurationData.Providers.Remove(provider);
|
||||
|
@ -4,7 +4,6 @@ using System.Text.Json;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.Anthropic;
|
||||
|
||||
@ -22,7 +21,7 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
|
||||
public string InstanceName { get; set; } = "Anthropic";
|
||||
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<string> StreamChatCompletion(IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
@ -137,14 +136,14 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(IJSRuntime jsRuntime, SettingsManager settings, Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
public Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(new[]
|
||||
{
|
||||
@ -157,7 +156,7 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<Model>> GetImageModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
public Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(Enumerable.Empty<Model>());
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.Fireworks;
|
||||
|
||||
@ -24,7 +23,7 @@ public class ProviderFireworks(ILogger logger) : BaseProvider("https://api.firew
|
||||
public string InstanceName { get; set; } = "Fireworks.ai";
|
||||
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<string> StreamChatCompletion(IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
@ -139,20 +138,20 @@ public class ProviderFireworks(ILogger logger) : BaseProvider("https://api.firew
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(IJSRuntime jsRuntime, SettingsManager settings, Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
public Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(Enumerable.Empty<Model>());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<Model>> GetImageModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
public Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(Enumerable.Empty<Model>());
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider;
|
||||
|
||||
@ -22,44 +21,36 @@ public interface IProvider
|
||||
/// <summary>
|
||||
/// Starts a chat completion stream.
|
||||
/// </summary>
|
||||
/// <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="chatModel">The model to use for chat completion.</param>
|
||||
/// <param name="chatThread">The chat thread to continue.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The chat completion stream.</returns>
|
||||
public IAsyncEnumerable<string> StreamChatCompletion(IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread chatThread, CancellationToken token = default);
|
||||
public IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Starts an image completion stream.
|
||||
/// </summary>
|
||||
/// <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="imageModel">The model to use for image completion.</param>
|
||||
/// <param name="promptPositive">The positive prompt.</param>
|
||||
/// <param name="promptNegative">The negative prompt.</param>
|
||||
/// <param name="referenceImageURL">The reference image URL.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The image completion stream.</returns>
|
||||
public IAsyncEnumerable<ImageURL> StreamImageCompletion(IJSRuntime jsRuntime, SettingsManager settings, Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, CancellationToken token = default);
|
||||
public IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Load all possible text models that can be used with this provider.
|
||||
/// </summary>
|
||||
/// <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="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>
|
||||
/// <returns>The list of text models.</returns>
|
||||
public Task<IEnumerable<Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default);
|
||||
public Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Load all possible image models that can be used with this provider.
|
||||
/// </summary>
|
||||
/// <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="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>
|
||||
/// <returns>The list of image models.</returns>
|
||||
public Task<IEnumerable<Model>> GetImageModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default);
|
||||
public Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default);
|
||||
}
|
@ -5,7 +5,6 @@ using System.Text.Json;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.Mistral;
|
||||
|
||||
@ -23,7 +22,7 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
public string InstanceName { get; set; } = "Mistral";
|
||||
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<string> StreamChatCompletion(IJSRuntime jsRuntime, SettingsManager settings, Provider.Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public async IAsyncEnumerable<string> StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
@ -141,14 +140,14 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(IJSRuntime jsRuntime, SettingsManager settings, Provider.Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(Provider.Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IEnumerable<Provider.Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
public async Task<IEnumerable<Provider.Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
var secretKey = apiKeyProvisional switch
|
||||
{
|
||||
@ -179,7 +178,7 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<Provider.Model>> GetImageModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
public Task<IEnumerable<Provider.Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(Enumerable.Empty<Provider.Model>());
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider;
|
||||
|
||||
@ -13,17 +12,17 @@ public class NoProvider : IProvider
|
||||
|
||||
public string InstanceName { get; set; } = "None";
|
||||
|
||||
public Task<IEnumerable<Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default) => Task.FromResult<IEnumerable<Model>>([]);
|
||||
public Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, 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 Task<IEnumerable<Model>> GetImageModels(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(Model chatModel, ChatThread chatChatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
await Task.FromResult(0);
|
||||
yield break;
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(IJSRuntime jsRuntime, SettingsManager settings, Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
await Task.FromResult(0);
|
||||
yield break;
|
||||
|
@ -4,7 +4,6 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
@ -27,7 +26,7 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
|
||||
public string InstanceName { get; set; } = "OpenAI";
|
||||
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<string> StreamChatCompletion(IJSRuntime jsRuntime, SettingsManager settings, Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public async IAsyncEnumerable<string> StreamChatCompletion(Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Get the API key:
|
||||
var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
|
||||
@ -145,20 +144,20 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(IJSRuntime jsRuntime, SettingsManager settings, Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
public Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
return this.LoadModels("gpt-", token, apiKeyProvisional);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<Model>> GetImageModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
public Task<IEnumerable<Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
return this.LoadModels("dall-e-", token, apiKeyProvisional);
|
||||
}
|
||||
|
@ -4,8 +4,6 @@ using AIStudio.Provider.Mistral;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
using AIStudio.Provider.SelfHosted;
|
||||
|
||||
using RustService = AIStudio.Tools.RustService;
|
||||
|
||||
namespace AIStudio.Provider;
|
||||
|
||||
public static class ProvidersExtensions
|
||||
@ -35,9 +33,8 @@ public static class ProvidersExtensions
|
||||
/// </summary>
|
||||
/// <param name="providerSettings">The provider settings.</param>
|
||||
/// <param name="logger">The logger to use.</param>
|
||||
/// <param name="rustService">The Rust instance to use.</param>
|
||||
/// <returns>The provider instance.</returns>
|
||||
public static IProvider CreateProvider(this Settings.Provider providerSettings, ILogger logger, RustService rustService)
|
||||
public static IProvider CreateProvider(this Settings.Provider providerSettings, ILogger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -4,7 +4,6 @@ using System.Text.Json;
|
||||
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider.OpenAI;
|
||||
using AIStudio.Settings;
|
||||
|
||||
namespace AIStudio.Provider.SelfHosted;
|
||||
|
||||
@ -21,7 +20,8 @@ public sealed class ProviderSelfHosted(ILogger logger, Settings.Provider provide
|
||||
|
||||
public string InstanceName { get; set; } = "Self-hosted";
|
||||
|
||||
public async IAsyncEnumerable<string> StreamChatCompletion(IJSRuntime jsRuntime, SettingsManager settings, Provider.Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<string> StreamChatCompletion(Provider.Model chatModel, ChatThread chatThread, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
// Prepare the system prompt:
|
||||
var systemPrompt = new Message
|
||||
@ -129,14 +129,14 @@ public sealed class ProviderSelfHosted(ILogger logger, Settings.Provider provide
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(IJSRuntime jsRuntime, SettingsManager settings, Provider.Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
public async IAsyncEnumerable<ImageURL> StreamImageCompletion(Provider.Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, [EnumeratorCancellation] CancellationToken token = default)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
|
||||
public async Task<IEnumerable<Provider.Model>> GetTextModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
public async Task<IEnumerable<Provider.Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -169,7 +169,7 @@ public sealed class ProviderSelfHosted(ILogger logger, Settings.Provider provide
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
/// <inheritdoc />
|
||||
public Task<IEnumerable<Provider.Model>> GetImageModels(IJSRuntime jsRuntime, SettingsManager settings, string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
public Task<IEnumerable<Provider.Model>> GetImageModels(string? apiKeyProvisional = null, CancellationToken token = default)
|
||||
{
|
||||
return Task.FromResult(Enumerable.Empty<Provider.Model>());
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings.DataModel;
|
||||
|
||||
// ReSharper disable NotAccessedPositionalProperty.Local
|
||||
@ -40,28 +39,6 @@ public sealed class SettingsManager(ILogger<SettingsManager> logger)
|
||||
|
||||
private bool IsSetUp => !string.IsNullOrWhiteSpace(ConfigDirectory) && !string.IsNullOrWhiteSpace(DataDirectory);
|
||||
|
||||
#region API Key Handling
|
||||
|
||||
private readonly record struct DeleteSecretRequest(string Destination, string UserName);
|
||||
|
||||
/// <summary>
|
||||
/// Data structure for deleting a secret response.
|
||||
/// </summary>
|
||||
/// <param name="Success">True, when the secret was successfully deleted or not found.</param>
|
||||
/// <param name="Issue">The issue, when the secret could not be deleted.</param>
|
||||
/// <param name="WasEntryFound">True, when the entry was found and deleted.</param>
|
||||
public readonly record struct DeleteSecretResponse(bool Success, string Issue, bool WasEntryFound);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to delete the API key for the given provider.
|
||||
/// </summary>
|
||||
/// <param name="jsRuntime">The JS runtime to access the Rust code.</param>
|
||||
/// <param name="provider">The provider to delete the API key for.</param>
|
||||
/// <returns>The delete secret response.</returns>
|
||||
public async Task<DeleteSecretResponse> DeleteAPIKey(IJSRuntime jsRuntime, IProvider provider) => await jsRuntime.InvokeAsync<DeleteSecretResponse>("window.__TAURI__.invoke", "delete_secret", new DeleteSecretRequest($"provider::{provider.Id}::{provider.InstanceName}::api_key", Environment.UserName));
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Loads the settings from the file system.
|
||||
/// </summary>
|
||||
|
@ -0,0 +1,9 @@
|
||||
namespace AIStudio.Tools.Rust;
|
||||
|
||||
/// <summary>
|
||||
/// Data structure for deleting a secret response.
|
||||
/// </summary>
|
||||
/// <param name="Success">True, when the secret was successfully deleted or not found.</param>
|
||||
/// <param name="Issue">The issue, when the secret could not be deleted.</param>
|
||||
/// <param name="WasEntryFound">True, when the entry was found and deleted.</param>
|
||||
public readonly record struct DeleteSecretResponse(bool Success, string Issue, bool WasEntryFound);
|
@ -1,6 +0,0 @@
|
||||
namespace AIStudio.Tools.Rust;
|
||||
|
||||
public readonly record struct GetSecretRequest(
|
||||
string Destination,
|
||||
string UserName
|
||||
);
|
3
app/MindWork AI Studio/Tools/Rust/SelectSecretRequest.cs
Normal file
3
app/MindWork AI Studio/Tools/Rust/SelectSecretRequest.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace AIStudio.Tools.Rust;
|
||||
|
||||
public readonly record struct SelectSecretRequest(string Destination, string UserName);
|
@ -170,7 +170,7 @@ public sealed class RustService(string apiPort) : IDisposable
|
||||
/// <returns>The requested secret.</returns>
|
||||
public async Task<RequestedSecret> GetAPIKey(IProvider provider)
|
||||
{
|
||||
var secretRequest = new GetSecretRequest($"provider::{provider.Id}::{provider.InstanceName}::api_key", Environment.UserName);
|
||||
var secretRequest = new SelectSecretRequest($"provider::{provider.Id}::{provider.InstanceName}::api_key", Environment.UserName);
|
||||
var result = await this.http.PostAsJsonAsync("/secrets/get", secretRequest, this.jsonRustSerializerOptions);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
@ -209,6 +209,27 @@ public sealed class RustService(string apiPort) : IDisposable
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to delete the API key for the given provider.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider to delete the API key for.</param>
|
||||
/// <returns>The delete secret response.</returns>
|
||||
public async Task<DeleteSecretResponse> DeleteAPIKey(IProvider provider)
|
||||
{
|
||||
var request = new SelectSecretRequest($"provider::{provider.Id}::{provider.InstanceName}::api_key", Environment.UserName);
|
||||
var result = await this.http.PostAsJsonAsync("/secrets/delete", request, this.jsonRustSerializerOptions);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
this.logger!.LogError($"Failed to delete the API key for provider '{provider.Id}' due to an API issue: '{result.StatusCode}'");
|
||||
return new DeleteSecretResponse{Success = false, WasEntryFound = false, Issue = "Failed to delete the API key due to an API issue."};
|
||||
}
|
||||
|
||||
var state = await result.Content.ReadFromJsonAsync<DeleteSecretResponse>();
|
||||
if (!state.Success)
|
||||
this.logger!.LogError($"Failed to delete the API key for provider '{provider.Id}': '{state.Issue}'");
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
|
@ -194,7 +194,10 @@ async fn main() {
|
||||
//
|
||||
tauri::async_runtime::spawn(async move {
|
||||
_ = rocket::custom(figment)
|
||||
.mount("/", routes![dotnet_port, dotnet_ready, set_clipboard, check_for_update, install_update, get_secret, store_secret])
|
||||
.mount("/", routes![
|
||||
dotnet_port, dotnet_ready, set_clipboard, check_for_update, install_update,
|
||||
get_secret, store_secret, delete_secret
|
||||
])
|
||||
.ignite().await.unwrap()
|
||||
.launch().await.unwrap();
|
||||
});
|
||||
@ -318,9 +321,6 @@ async fn main() {
|
||||
Ok(())
|
||||
})
|
||||
.plugin(tauri_plugin_window_state::Builder::default().build())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
delete_secret
|
||||
])
|
||||
.build(tauri::generate_context!())
|
||||
.expect("Error while running Tauri application");
|
||||
|
||||
@ -849,38 +849,39 @@ struct RequestedSecret {
|
||||
issue: String,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn delete_secret(destination: String, user_name: String) -> DeleteSecretResponse {
|
||||
let service = format!("mindwork-ai-studio::{}", destination);
|
||||
let entry = Entry::new(service.as_str(), user_name.as_str()).unwrap();
|
||||
#[post("/secrets/delete", data = "<request>")]
|
||||
fn delete_secret(request: Json<RequestSecret>) -> Json<DeleteSecretResponse> {
|
||||
let user_name = request.user_name.as_str();
|
||||
let service = format!("mindwork-ai-studio::{}", request.destination);
|
||||
let entry = Entry::new(service.as_str(), user_name).unwrap();
|
||||
let result = entry.delete_credential();
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
warn!(Source = "Secret Store"; "Secret for {service} and user {user_name} was deleted successfully.");
|
||||
DeleteSecretResponse {
|
||||
Json(DeleteSecretResponse {
|
||||
success: true,
|
||||
was_entry_found: true,
|
||||
issue: String::from(""),
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
Err(NoEntry) => {
|
||||
warn!(Source = "Secret Store"; "No secret for {service} and user {user_name} was found.");
|
||||
DeleteSecretResponse {
|
||||
Json(DeleteSecretResponse {
|
||||
success: true,
|
||||
was_entry_found: false,
|
||||
issue: String::from(""),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
error!(Source = "Secret Store"; "Failed to delete secret for {service} and user {user_name}: {e}.");
|
||||
DeleteSecretResponse {
|
||||
Json(DeleteSecretResponse {
|
||||
success: false,
|
||||
was_entry_found: false,
|
||||
issue: e.to_string(),
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user