mirror of
				https://github.com/MindWorkAI/AI-Studio.git
				synced 2025-11-04 01:20:21 +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.Provider;
 | 
				
			||||||
using AIStudio.Settings;
 | 
					using AIStudio.Settings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using RustService = AIStudio.Tools.RustService;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ReSharper disable MemberCanBePrivate.Global
 | 
					// ReSharper disable MemberCanBePrivate.Global
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace AIStudio.Agents;
 | 
					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 SettingsManager SettingsManager { get; init; } = settingsManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected IJSRuntime JsRuntime { get; init; } = jsRuntime;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected ThreadSafeRandom RNG { get; init; } = rng;
 | 
					    protected ThreadSafeRandom RNG { get; init; } = rng;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    protected ILogger<AgentBase> Logger { get; init; } = logger;
 | 
					    protected ILogger<AgentBase> Logger { get; init; } = logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected RustService RustService { get; init; } = rustService;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// Represents the type or category of this agent.
 | 
					    /// Represents the type or category of this agent.
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
@ -109,6 +103,6 @@ public abstract class AgentBase(ILogger<AgentBase> logger, RustService rustServi
 | 
				
			|||||||
        // Use the selected provider to get the AI response.
 | 
					        // Use the selected provider to get the AI response.
 | 
				
			||||||
        // By awaiting this line, we wait for the entire
 | 
					        // By awaiting this line, we wait for the entire
 | 
				
			||||||
        // content to be streamed.
 | 
					        // 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.Chat;
 | 
				
			||||||
using AIStudio.Settings;
 | 
					using AIStudio.Settings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using RustService = AIStudio.Tools.RustService;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace AIStudio.Agents;
 | 
					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()
 | 
					    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.
 | 
					        // Use the selected provider to get the AI response.
 | 
				
			||||||
        // By awaiting this line, we wait for the entire
 | 
					        // By awaiting this line, we wait for the entire
 | 
				
			||||||
        // content to be streamed.
 | 
					        // 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.isProcessing = false;
 | 
				
			||||||
        this.StateHasChanged();
 | 
					        this.StateHasChanged();
 | 
				
			||||||
 | 
				
			|||||||
@ -42,9 +42,6 @@ public partial class ContentBlockComponent : ComponentBase
 | 
				
			|||||||
    [Inject]
 | 
					    [Inject]
 | 
				
			||||||
    private RustService RustService { get; init; } = null!;
 | 
					    private RustService RustService { get; init; } = null!;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    [Inject]
 | 
					 | 
				
			||||||
    private IJSRuntime JsRuntime { get; init; } = null!;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    [Inject]
 | 
					    [Inject]
 | 
				
			||||||
    private ISnackbar Snackbar { get; init; } = null!;
 | 
					    private ISnackbar Snackbar { get; init; } = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -29,7 +29,7 @@ public sealed class ContentImage : IContent
 | 
				
			|||||||
    public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
 | 
					    public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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();
 | 
					        throw new NotImplementedException();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -35,18 +35,18 @@ public sealed class ContentText : IContent
 | 
				
			|||||||
    public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
 | 
					    public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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)
 | 
					        if(chatThread is null)
 | 
				
			||||||
            return;
 | 
					            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
 | 
					        // to determine whether we should notify the UI about the
 | 
				
			||||||
        // new content or not. Depends on the energy saving mode
 | 
					        // new content or not. Depends on the energy saving mode
 | 
				
			||||||
        // the user chose.
 | 
					        // the user chose.
 | 
				
			||||||
        var last = DateTimeOffset.Now;
 | 
					        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:
 | 
					        // the UI thread from the AI processing:
 | 
				
			||||||
        await Task.Run(async () =>
 | 
					        await Task.Run(async () =>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -54,7 +54,7 @@ public sealed class ContentText : IContent
 | 
				
			|||||||
            this.InitialRemoteWait = true;
 | 
					            this.InitialRemoteWait = true;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // Iterate over the responses from the AI:
 | 
					            // 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:
 | 
					                // When the user cancels the request, we stop the loop:
 | 
				
			||||||
                if (token.IsCancellationRequested)
 | 
					                if (token.IsCancellationRequested)
 | 
				
			||||||
@ -89,7 +89,7 @@ public sealed class ContentText : IContent
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Stop the waiting animation (in case the loop
 | 
					            // 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.InitialRemoteWait = false;
 | 
				
			||||||
            this.IsStreaming = false;
 | 
					            this.IsStreaming = false;
 | 
				
			||||||
        }, token);
 | 
					        }, token);
 | 
				
			||||||
 | 
				
			|||||||
@ -42,5 +42,5 @@ public interface IContent
 | 
				
			|||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// Uses the provider to create the content.
 | 
					    /// Uses the provider to create the content.
 | 
				
			||||||
    /// </summary>
 | 
					    /// </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
 | 
				
			||||||
@using AIStudio.Provider.SelfHosted
 | 
					@using AIStudio.Provider.SelfHosted
 | 
				
			||||||
@using MudBlazor
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
<MudDialog>
 | 
					<MudDialog>
 | 
				
			||||||
    <DialogContent>
 | 
					    <DialogContent>
 | 
				
			||||||
 | 
				
			|||||||
@ -75,9 +75,6 @@ public partial class ProviderDialog : ComponentBase
 | 
				
			|||||||
    [Inject]
 | 
					    [Inject]
 | 
				
			||||||
    private SettingsManager SettingsManager { get; init; } = null!;
 | 
					    private SettingsManager SettingsManager { get; init; } = null!;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    [Inject]
 | 
					 | 
				
			||||||
    private IJSRuntime JsRuntime { get; init; } = null!;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    [Inject]
 | 
					    [Inject]
 | 
				
			||||||
    private ILogger<ProviderDialog> Logger { get; init; } = null!;
 | 
					    private ILogger<ProviderDialog> Logger { get; init; } = null!;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@ -142,7 +139,7 @@ public partial class ProviderDialog : ComponentBase
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            var loadedProviderSettings = this.CreateProviderSettings();
 | 
					            var loadedProviderSettings = this.CreateProviderSettings();
 | 
				
			||||||
            var provider = loadedProviderSettings.CreateProvider(this.Logger, this.RustService);
 | 
					            var provider = loadedProviderSettings.CreateProvider(this.Logger);
 | 
				
			||||||
            if(provider is NoProvider)
 | 
					            if(provider is NoProvider)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await base.OnInitializedAsync();
 | 
					                await base.OnInitializedAsync();
 | 
				
			||||||
@ -196,7 +193,7 @@ public partial class ProviderDialog : ComponentBase
 | 
				
			|||||||
        if (addedProviderSettings.UsedProvider != Providers.SELF_HOSTED)
 | 
					        if (addedProviderSettings.UsedProvider != Providers.SELF_HOSTED)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // We need to instantiate the provider to store the API key:
 | 
					            // 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:
 | 
					            // Store the API key in the OS secure storage:
 | 
				
			||||||
            var storeResponse = await this.RustService.SetAPIKey(provider, this.dataAPIKey);
 | 
					            var storeResponse = await this.RustService.SetAPIKey(provider, this.dataAPIKey);
 | 
				
			||||||
@ -327,11 +324,11 @@ public partial class ProviderDialog : ComponentBase
 | 
				
			|||||||
    private async Task ReloadModels()
 | 
					    private async Task ReloadModels()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var currentProviderSettings = this.CreateProviderSettings();
 | 
					        var currentProviderSettings = this.CreateProviderSettings();
 | 
				
			||||||
        var provider = currentProviderSettings.CreateProvider(this.Logger, this.RustService);
 | 
					        var provider = currentProviderSettings.CreateProvider(this.Logger);
 | 
				
			||||||
        if(provider is NoProvider)
 | 
					        if(provider is NoProvider)
 | 
				
			||||||
            return;
 | 
					            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:
 | 
					        // 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);
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Components;
 | 
				
			|||||||
using Microsoft.AspNetCore.Components.Web;
 | 
					using Microsoft.AspNetCore.Components.Web;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
 | 
					using DialogOptions = AIStudio.Dialogs.DialogOptions;
 | 
				
			||||||
using RustService = AIStudio.Tools.RustService;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace AIStudio.Pages;
 | 
					namespace AIStudio.Pages;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -21,9 +20,6 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
 | 
				
			|||||||
    [Inject]
 | 
					    [Inject]
 | 
				
			||||||
    private SettingsManager SettingsManager { get; init; } = null!;
 | 
					    private SettingsManager SettingsManager { get; init; } = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Inject]
 | 
					 | 
				
			||||||
    public IJSRuntime JsRuntime { get; init; } = null!;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    [Inject]
 | 
					    [Inject]
 | 
				
			||||||
    private ThreadSafeRandom RNG { get; init; } = null!;
 | 
					    private ThreadSafeRandom RNG { get; init; } = null!;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@ -33,9 +29,6 @@ public partial class Chat : MSGComponentBase, IAsyncDisposable
 | 
				
			|||||||
    [Inject]
 | 
					    [Inject]
 | 
				
			||||||
    private ILogger<Chat> Logger { get; init; } = null!;
 | 
					    private ILogger<Chat> Logger { get; init; } = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Inject]
 | 
					 | 
				
			||||||
    private RustService RustService { get; init; } = null!;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    private InnerScrolling scrollingArea = null!;
 | 
					    private InnerScrolling scrollingArea = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private const Placement TOOLBAR_TOOLTIP_PLACEMENT = Placement.Bottom;
 | 
					    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.
 | 
					        // Use the selected provider to get the AI response.
 | 
				
			||||||
        // By awaiting this line, we wait for the entire
 | 
					        // By awaiting this line, we wait for the entire
 | 
				
			||||||
        // content to be streamed.
 | 
					        // 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:
 | 
					        // Save the chat:
 | 
				
			||||||
        if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
 | 
					        if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
 | 
				
			||||||
 | 
				
			|||||||
@ -19,9 +19,6 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
 | 
				
			|||||||
    [Inject]
 | 
					    [Inject]
 | 
				
			||||||
    private IDialogService DialogService { get; init; } = null!;
 | 
					    private IDialogService DialogService { get; init; } = null!;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    [Inject]
 | 
					 | 
				
			||||||
    private IJSRuntime JsRuntime { get; init; } = null!;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    [Inject]
 | 
					    [Inject]
 | 
				
			||||||
    private MessageBus MessageBus { get; init; } = null!;
 | 
					    private MessageBus MessageBus { get; init; } = null!;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@ -117,8 +114,8 @@ public partial class Settings : ComponentBase, IMessageBusReceiver, IDisposable
 | 
				
			|||||||
        if (dialogResult is null || dialogResult.Canceled)
 | 
					        if (dialogResult is null || dialogResult.Canceled)
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        var providerInstance = provider.CreateProvider(this.Logger, this.RustService);
 | 
					        var providerInstance = provider.CreateProvider(this.Logger);
 | 
				
			||||||
        var deleteSecretResponse = await this.SettingsManager.DeleteAPIKey(this.JsRuntime, providerInstance);
 | 
					        var deleteSecretResponse = await this.RustService.DeleteAPIKey(providerInstance);
 | 
				
			||||||
        if(deleteSecretResponse.Success)
 | 
					        if(deleteSecretResponse.Success)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            this.SettingsManager.ConfigurationData.Providers.Remove(provider);
 | 
					            this.SettingsManager.ConfigurationData.Providers.Remove(provider);
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,6 @@ using System.Text.Json;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using AIStudio.Chat;
 | 
					using AIStudio.Chat;
 | 
				
			||||||
using AIStudio.Provider.OpenAI;
 | 
					using AIStudio.Provider.OpenAI;
 | 
				
			||||||
using AIStudio.Settings;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace AIStudio.Provider.Anthropic;
 | 
					namespace AIStudio.Provider.Anthropic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -22,7 +21,7 @@ public sealed class ProviderAnthropic(ILogger logger) : BaseProvider("https://ap
 | 
				
			|||||||
    public string InstanceName { get; set; } = "Anthropic";
 | 
					    public string InstanceName { get; set; } = "Anthropic";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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:
 | 
					        // Get the API key:
 | 
				
			||||||
        var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
 | 
					        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
 | 
					    #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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;
 | 
					        yield break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    #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, string? apiKeyProvisional = null, CancellationToken token = default)
 | 
					    public Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return Task.FromResult(new[]
 | 
					        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
 | 
					    #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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>());
 | 
					        return Task.FromResult(Enumerable.Empty<Model>());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,6 @@ using System.Text;
 | 
				
			|||||||
using System.Text.Json;
 | 
					using System.Text.Json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using AIStudio.Chat;
 | 
					using AIStudio.Chat;
 | 
				
			||||||
using AIStudio.Settings;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace AIStudio.Provider.Fireworks;
 | 
					namespace AIStudio.Provider.Fireworks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -24,7 +23,7 @@ public class ProviderFireworks(ILogger logger) : BaseProvider("https://api.firew
 | 
				
			|||||||
    public string InstanceName { get; set; } = "Fireworks.ai";
 | 
					    public string InstanceName { get; set; } = "Fireworks.ai";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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:
 | 
					        // Get the API key:
 | 
				
			||||||
        var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
 | 
					        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
 | 
					    #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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;
 | 
					        yield break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    #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, string? apiKeyProvisional = null, CancellationToken token = default)
 | 
					    public Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return Task.FromResult(Enumerable.Empty<Model>());
 | 
					        return Task.FromResult(Enumerable.Empty<Model>());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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>());
 | 
					        return Task.FromResult(Enumerable.Empty<Model>());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
using AIStudio.Chat;
 | 
					using AIStudio.Chat;
 | 
				
			||||||
using AIStudio.Settings;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace AIStudio.Provider;
 | 
					namespace AIStudio.Provider;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -22,44 +21,36 @@ public interface IProvider
 | 
				
			|||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// Starts a chat completion stream.
 | 
					    /// Starts a chat completion stream.
 | 
				
			||||||
    /// </summary>
 | 
					    /// </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="chatModel">The model to use for chat completion.</param>
 | 
				
			||||||
    /// <param name="chatThread">The chat thread to continue.</param>
 | 
					    /// <param name="chatThread">The chat thread to continue.</param>
 | 
				
			||||||
    /// <param name="token">The cancellation token.</param>
 | 
					    /// <param name="token">The cancellation token.</param>
 | 
				
			||||||
    /// <returns>The chat completion stream.</returns>
 | 
					    /// <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>
 | 
					    /// <summary>
 | 
				
			||||||
    /// Starts an image completion stream.
 | 
					    /// Starts an image completion stream.
 | 
				
			||||||
    /// </summary>
 | 
					    /// </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="imageModel">The model to use for image completion.</param>
 | 
				
			||||||
    /// <param name="promptPositive">The positive prompt.</param>
 | 
					    /// <param name="promptPositive">The positive prompt.</param>
 | 
				
			||||||
    /// <param name="promptNegative">The negative prompt.</param>
 | 
					    /// <param name="promptNegative">The negative prompt.</param>
 | 
				
			||||||
    /// <param name="referenceImageURL">The reference image URL.</param>
 | 
					    /// <param name="referenceImageURL">The reference image URL.</param>
 | 
				
			||||||
    /// <param name="token">The cancellation token.</param>
 | 
					    /// <param name="token">The cancellation token.</param>
 | 
				
			||||||
    /// <returns>The image completion stream.</returns>
 | 
					    /// <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>
 | 
					    /// <summary>
 | 
				
			||||||
    /// Load all possible text models that can be used with this provider.
 | 
					    /// Load all possible text models that can be used with this provider.
 | 
				
			||||||
    /// </summary>
 | 
					    /// </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="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, string? apiKeyProvisional = null, CancellationToken token = default);
 | 
					    public Task<IEnumerable<Model>> GetTextModels(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="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="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, 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.Chat;
 | 
				
			||||||
using AIStudio.Provider.OpenAI;
 | 
					using AIStudio.Provider.OpenAI;
 | 
				
			||||||
using AIStudio.Settings;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace AIStudio.Provider.Mistral;
 | 
					namespace AIStudio.Provider.Mistral;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -23,7 +22,7 @@ public sealed class ProviderMistral(ILogger logger) : BaseProvider("https://api.
 | 
				
			|||||||
    public string InstanceName { get; set; } = "Mistral";
 | 
					    public string InstanceName { get; set; } = "Mistral";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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:
 | 
					        // Get the API key:
 | 
				
			||||||
        var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
 | 
					        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
 | 
					    #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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;
 | 
					        yield break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    #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 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
 | 
					        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
 | 
					    #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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>());
 | 
					        return Task.FromResult(Enumerable.Empty<Provider.Model>());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
using System.Runtime.CompilerServices;
 | 
					using System.Runtime.CompilerServices;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using AIStudio.Chat;
 | 
					using AIStudio.Chat;
 | 
				
			||||||
using AIStudio.Settings;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace AIStudio.Provider;
 | 
					namespace AIStudio.Provider;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -13,17 +12,17 @@ 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, 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);
 | 
					        await Task.FromResult(0);
 | 
				
			||||||
        yield break;
 | 
					        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);
 | 
					        await Task.FromResult(0);
 | 
				
			||||||
        yield break;
 | 
					        yield break;
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,6 @@ using System.Text;
 | 
				
			|||||||
using System.Text.Json;
 | 
					using System.Text.Json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using AIStudio.Chat;
 | 
					using AIStudio.Chat;
 | 
				
			||||||
using AIStudio.Settings;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace AIStudio.Provider.OpenAI;
 | 
					namespace AIStudio.Provider.OpenAI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -27,7 +26,7 @@ public sealed class ProviderOpenAI(ILogger logger) : BaseProvider("https://api.o
 | 
				
			|||||||
    public string InstanceName { get; set; } = "OpenAI";
 | 
					    public string InstanceName { get; set; } = "OpenAI";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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:
 | 
					        // Get the API key:
 | 
				
			||||||
        var requestedSecret = await RUST_SERVICE.GetAPIKey(this);
 | 
					        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
 | 
					    #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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;
 | 
					        yield break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    #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, string? apiKeyProvisional = null, CancellationToken token = default)
 | 
					    public Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return this.LoadModels("gpt-", token, apiKeyProvisional);
 | 
					        return this.LoadModels("gpt-", token, apiKeyProvisional);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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);
 | 
					        return this.LoadModels("dall-e-", token, apiKeyProvisional);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -4,8 +4,6 @@ using AIStudio.Provider.Mistral;
 | 
				
			|||||||
using AIStudio.Provider.OpenAI;
 | 
					using AIStudio.Provider.OpenAI;
 | 
				
			||||||
using AIStudio.Provider.SelfHosted;
 | 
					using AIStudio.Provider.SelfHosted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using RustService = AIStudio.Tools.RustService;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace AIStudio.Provider;
 | 
					namespace AIStudio.Provider;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public static class ProvidersExtensions
 | 
					public static class ProvidersExtensions
 | 
				
			||||||
@ -35,9 +33,8 @@ public static class ProvidersExtensions
 | 
				
			|||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    /// <param name="providerSettings">The provider settings.</param>
 | 
					    /// <param name="providerSettings">The provider settings.</param>
 | 
				
			||||||
    /// <param name="logger">The logger to use.</param>
 | 
					    /// <param name="logger">The logger to use.</param>
 | 
				
			||||||
    /// <param name="rustService">The Rust instance to use.</param>
 | 
					 | 
				
			||||||
    /// <returns>The provider instance.</returns>
 | 
					    /// <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
 | 
					        try
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,6 @@ using System.Text.Json;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using AIStudio.Chat;
 | 
					using AIStudio.Chat;
 | 
				
			||||||
using AIStudio.Provider.OpenAI;
 | 
					using AIStudio.Provider.OpenAI;
 | 
				
			||||||
using AIStudio.Settings;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace AIStudio.Provider.SelfHosted;
 | 
					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 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:
 | 
					        // Prepare the system prompt:
 | 
				
			||||||
        var systemPrompt = new Message
 | 
					        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
 | 
					    #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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;
 | 
					        yield break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    #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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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
 | 
					        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
 | 
					    #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
 | 
				
			||||||
    /// <inheritdoc />
 | 
					    /// <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>());
 | 
					        return Task.FromResult(Enumerable.Empty<Provider.Model>());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
using System.Text.Json;
 | 
					using System.Text.Json;
 | 
				
			||||||
using System.Text.Json.Serialization;
 | 
					using System.Text.Json.Serialization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using AIStudio.Provider;
 | 
					 | 
				
			||||||
using AIStudio.Settings.DataModel;
 | 
					using AIStudio.Settings.DataModel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ReSharper disable NotAccessedPositionalProperty.Local
 | 
					// ReSharper disable NotAccessedPositionalProperty.Local
 | 
				
			||||||
@ -40,28 +39,6 @@ public sealed class SettingsManager(ILogger<SettingsManager> logger)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private bool IsSetUp => !string.IsNullOrWhiteSpace(ConfigDirectory) && !string.IsNullOrWhiteSpace(DataDirectory);
 | 
					    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>
 | 
					    /// <summary>
 | 
				
			||||||
    /// Loads the settings from the file system.
 | 
					    /// Loads the settings from the file system.
 | 
				
			||||||
    /// </summary>
 | 
					    /// </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>
 | 
					    /// <returns>The requested secret.</returns>
 | 
				
			||||||
    public async Task<RequestedSecret> GetAPIKey(IProvider provider)
 | 
					    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);
 | 
					        var result = await this.http.PostAsJsonAsync("/secrets/get", secretRequest, this.jsonRustSerializerOptions);
 | 
				
			||||||
        if (!result.IsSuccessStatusCode)
 | 
					        if (!result.IsSuccessStatusCode)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -209,6 +209,27 @@ public sealed class RustService(string apiPort) : IDisposable
 | 
				
			|||||||
        return state;
 | 
					        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
 | 
					    #region IDisposable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -194,7 +194,10 @@ async fn main() {
 | 
				
			|||||||
    //
 | 
					    //
 | 
				
			||||||
    tauri::async_runtime::spawn(async move {
 | 
					    tauri::async_runtime::spawn(async move {
 | 
				
			||||||
        _ = rocket::custom(figment)
 | 
					        _ = 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()
 | 
					            .ignite().await.unwrap()
 | 
				
			||||||
            .launch().await.unwrap();
 | 
					            .launch().await.unwrap();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@ -318,9 +321,6 @@ async fn main() {
 | 
				
			|||||||
            Ok(())
 | 
					            Ok(())
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .plugin(tauri_plugin_window_state::Builder::default().build())
 | 
					        .plugin(tauri_plugin_window_state::Builder::default().build())
 | 
				
			||||||
        .invoke_handler(tauri::generate_handler![
 | 
					 | 
				
			||||||
            delete_secret
 | 
					 | 
				
			||||||
        ])
 | 
					 | 
				
			||||||
        .build(tauri::generate_context!())
 | 
					        .build(tauri::generate_context!())
 | 
				
			||||||
        .expect("Error while running Tauri application");
 | 
					        .expect("Error while running Tauri application");
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@ -849,38 +849,39 @@ struct RequestedSecret {
 | 
				
			|||||||
    issue: String,
 | 
					    issue: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[tauri::command]
 | 
					#[post("/secrets/delete", data = "<request>")]
 | 
				
			||||||
fn delete_secret(destination: String, user_name: String) -> DeleteSecretResponse {
 | 
					fn delete_secret(request: Json<RequestSecret>) -> Json<DeleteSecretResponse> {
 | 
				
			||||||
    let service = format!("mindwork-ai-studio::{}", destination);
 | 
					    let user_name = request.user_name.as_str();
 | 
				
			||||||
    let entry = Entry::new(service.as_str(), user_name.as_str()).unwrap();
 | 
					    let service = format!("mindwork-ai-studio::{}", request.destination);
 | 
				
			||||||
 | 
					    let entry = Entry::new(service.as_str(), user_name).unwrap();
 | 
				
			||||||
    let result = entry.delete_credential();
 | 
					    let result = entry.delete_credential();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    match result {
 | 
					    match result {
 | 
				
			||||||
        Ok(_) => {
 | 
					        Ok(_) => {
 | 
				
			||||||
            warn!(Source = "Secret Store"; "Secret for {service} and user {user_name} was deleted successfully.");
 | 
					            warn!(Source = "Secret Store"; "Secret for {service} and user {user_name} was deleted successfully.");
 | 
				
			||||||
            DeleteSecretResponse {
 | 
					            Json(DeleteSecretResponse {
 | 
				
			||||||
                success: true,
 | 
					                success: true,
 | 
				
			||||||
                was_entry_found: true,
 | 
					                was_entry_found: true,
 | 
				
			||||||
                issue: String::from(""),
 | 
					                issue: String::from(""),
 | 
				
			||||||
            }
 | 
					            })
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Err(NoEntry) => {
 | 
					        Err(NoEntry) => {
 | 
				
			||||||
            warn!(Source = "Secret Store"; "No secret for {service} and user {user_name} was found.");
 | 
					            warn!(Source = "Secret Store"; "No secret for {service} and user {user_name} was found.");
 | 
				
			||||||
            DeleteSecretResponse {
 | 
					            Json(DeleteSecretResponse {
 | 
				
			||||||
                success: true,
 | 
					                success: true,
 | 
				
			||||||
                was_entry_found: false,
 | 
					                was_entry_found: false,
 | 
				
			||||||
                issue: String::from(""),
 | 
					                issue: String::from(""),
 | 
				
			||||||
            }
 | 
					            })
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Err(e) => {
 | 
					        Err(e) => {
 | 
				
			||||||
            error!(Source = "Secret Store"; "Failed to delete secret for {service} and user {user_name}: {e}.");
 | 
					            error!(Source = "Secret Store"; "Failed to delete secret for {service} and user {user_name}: {e}.");
 | 
				
			||||||
            DeleteSecretResponse {
 | 
					            Json(DeleteSecretResponse {
 | 
				
			||||||
                success: false,
 | 
					                success: false,
 | 
				
			||||||
                was_entry_found: false,
 | 
					                was_entry_found: false,
 | 
				
			||||||
                issue: e.to_string(),
 | 
					                issue: e.to_string(),
 | 
				
			||||||
            }
 | 
					            })
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user