From 1765a4d514f57255d02cee4a6065695cfb503d43 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Tue, 19 Aug 2025 20:37:45 +0200 Subject: [PATCH] Use `Provider.NONE` as a default value and refactor provider handling logic across components. --- app/MindWork AI Studio/Agents/AgentBase.cs | 7 +++---- .../Agents/AgentDataSourceSelection.cs | 7 ++++++- .../Agents/AgentRetrievalContextValidation.cs | 7 ++++++- .../Agents/AgentTextContentCleaner.cs | 2 +- app/MindWork AI Studio/Agents/IAgent.cs | 2 +- .../Assistants/AssistantBase.razor.cs | 4 ++-- .../Components/ChatComponent.razor.cs | 8 ++++---- .../ConfigurationProviderSelection.razor.cs | 3 +++ .../Components/ProviderSelection.razor.cs | 9 +++++---- .../Components/ReadWebContent.razor.cs | 8 ++++---- .../Settings/SettingsPanelProviders.razor.cs | 3 +++ app/MindWork AI Studio/Pages/Chat.razor.cs | 2 +- app/MindWork AI Studio/Pages/Writer.razor.cs | 2 +- app/MindWork AI Studio/Settings/Provider.cs | 17 ++++++++++++++++- .../Settings/SettingsManager.cs | 16 ++++++++-------- .../Tools/ComponentsExtensions.cs | 2 +- .../Tools/Services/DataSourceService.cs | 2 +- 17 files changed, 66 insertions(+), 35 deletions(-) diff --git a/app/MindWork AI Studio/Agents/AgentBase.cs b/app/MindWork AI Studio/Agents/AgentBase.cs index 66f13146..2b9239d1 100644 --- a/app/MindWork AI Studio/Agents/AgentBase.cs +++ b/app/MindWork AI Studio/Agents/AgentBase.cs @@ -54,7 +54,7 @@ public abstract class AgentBase(ILogger logger, SettingsManager setti #region Implementation of IAgent - public abstract AIStudio.Settings.Provider? ProviderSettings { get; set; } + public abstract AIStudio.Settings.Provider ProviderSettings { get; set; } public abstract Task ProcessContext(ChatThread chatThread, IDictionary additionalData); @@ -103,10 +103,9 @@ public abstract class AgentBase(ILogger logger, SettingsManager setti protected async Task AddAIResponseAsync(ChatThread thread, IContent lastUserPrompt, DateTimeOffset time) { - if(this.ProviderSettings is null) + if(this.ProviderSettings == Settings.Provider.NONE) return; - var providerSettings = this.ProviderSettings.Value; var aiText = new ContentText { // We have to wait for the remote @@ -127,6 +126,6 @@ public abstract class AgentBase(ILogger logger, SettingsManager setti // 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), providerSettings.Model, lastUserPrompt, thread); + await aiText.CreateFromProviderAsync(this.ProviderSettings.CreateProvider(this.Logger), this.ProviderSettings.Model, lastUserPrompt, thread); } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Agents/AgentDataSourceSelection.cs b/app/MindWork AI Studio/Agents/AgentDataSourceSelection.cs index 6fe1f4e2..42d395be 100644 --- a/app/MindWork AI Studio/Agents/AgentDataSourceSelection.cs +++ b/app/MindWork AI Studio/Agents/AgentDataSourceSelection.cs @@ -86,7 +86,7 @@ public sealed class AgentDataSourceSelection (ILogger """; /// - public override Settings.Provider? ProviderSettings { get; set; } + public override Settings.Provider ProviderSettings { get; set; } = Settings.Provider.NONE; /// /// The data source selection agent does not work with context. Use @@ -141,6 +141,11 @@ public sealed class AgentDataSourceSelection (ILogger // We start with the provider currently selected by the user: var agentProvider = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_DATA_SOURCE_SELECTION, provider.Id, true); + if (agentProvider == Settings.Provider.NONE) + { + logger.LogWarning("No provider is selected for the agent. The agent cannot select data sources."); + return []; + } // Assign the provider settings to the agent: logger.LogInformation($"The agent for the data source selection uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()})."); diff --git a/app/MindWork AI Studio/Agents/AgentRetrievalContextValidation.cs b/app/MindWork AI Studio/Agents/AgentRetrievalContextValidation.cs index 9a4fdff5..7d7b9bb6 100644 --- a/app/MindWork AI Studio/Agents/AgentRetrievalContextValidation.cs +++ b/app/MindWork AI Studio/Agents/AgentRetrievalContextValidation.cs @@ -71,7 +71,7 @@ public sealed class AgentRetrievalContextValidation (ILogger - public override Settings.Provider? ProviderSettings { get; set; } + public override Settings.Provider ProviderSettings { get; set; } = Settings.Provider.NONE; /// /// The retrieval context validation agent does not work with context. Use @@ -133,6 +133,11 @@ public sealed class AgentRetrievalContextValidation (ILogger logger, SettingsM #region Overrides of AgentBase - public override AIStudio.Settings.Provider? ProviderSettings { get; set; } + public override AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE; protected override Type Type => Type.SYSTEM; diff --git a/app/MindWork AI Studio/Agents/IAgent.cs b/app/MindWork AI Studio/Agents/IAgent.cs index e101f2ef..3ee77d8b 100644 --- a/app/MindWork AI Studio/Agents/IAgent.cs +++ b/app/MindWork AI Studio/Agents/IAgent.cs @@ -12,7 +12,7 @@ public interface IAgent /// /// The provider to use for this agent. /// - public AIStudio.Settings.Provider? ProviderSettings { get; set; } + public Settings.Provider ProviderSettings { get; set; } /// /// Processes a chat thread (i.e., context) and returns the updated thread. diff --git a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs index 39bf2659..12ad2757 100644 --- a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs +++ b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs @@ -85,7 +85,7 @@ public abstract partial class AssistantBase : AssistantLowerBase wher protected virtual IReadOnlyList FooterButtons => []; - protected AIStudio.Settings.Provider providerSettings; + protected AIStudio.Settings.Provider providerSettings = Settings.Provider.NONE; protected MudForm? form; protected bool inputIsValid; protected Profile currentProfile = Profile.NO_PROFILE; @@ -352,7 +352,7 @@ public abstract partial class AssistantBase : AssistantLowerBase wher private async Task InnerResetForm() { this.resultingContentBlock = null; - this.providerSettings = default; + this.providerSettings = Settings.Provider.NONE; await this.JsRuntime.ClearDiv(RESULT_DIV_ID); await this.JsRuntime.ClearDiv(AFTER_RESULT_DIV_ID); diff --git a/app/MindWork AI Studio/Components/ChatComponent.razor.cs b/app/MindWork AI Studio/Components/ChatComponent.razor.cs index 3c4a8d38..6af2a5b7 100644 --- a/app/MindWork AI Studio/Components/ChatComponent.razor.cs +++ b/app/MindWork AI Studio/Components/ChatComponent.razor.cs @@ -18,9 +18,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable [Parameter] public EventCallback ChatThreadChanged { get; set; } - + [Parameter] - public AIStudio.Settings.Provider Provider { get; set; } + public AIStudio.Settings.Provider Provider { get; set; } = AIStudio.Settings.Provider.NONE; [Parameter] public EventCallback ProviderChanged { get; set; } @@ -634,7 +634,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable default: case AddChatProviderBehavior.ADDED_CHATS_USE_LATEST_PROVIDER: - if(this.Provider == default) + if(this.Provider == AIStudio.Settings.Provider.NONE) { this.Provider = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT); await this.ProviderChanged.InvokeAsync(this.Provider); @@ -797,7 +797,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable break; case LoadingChatProviderBehavior.ALWAYS_USE_LATEST_CHAT_PROVIDER: - if(this.Provider == default) + if(this.Provider == AIStudio.Settings.Provider.NONE) this.Provider = this.SettingsManager.GetPreselectedProvider(Tools.Components.CHAT); break; } diff --git a/app/MindWork AI Studio/Components/ConfigurationProviderSelection.razor.cs b/app/MindWork AI Studio/Components/ConfigurationProviderSelection.razor.cs index 8eced0f2..39ae76be 100644 --- a/app/MindWork AI Studio/Components/ConfigurationProviderSelection.razor.cs +++ b/app/MindWork AI Studio/Components/ConfigurationProviderSelection.razor.cs @@ -42,6 +42,9 @@ public partial class ConfigurationProviderSelection : MSGComponentBase foreach (var providerId in this.Data) { var provider = this.SettingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == providerId.Value); + if (provider is null) + continue; + if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel) yield return providerId; } diff --git a/app/MindWork AI Studio/Components/ProviderSelection.razor.cs b/app/MindWork AI Studio/Components/ProviderSelection.razor.cs index b476b19f..ded13ebd 100644 --- a/app/MindWork AI Studio/Components/ProviderSelection.razor.cs +++ b/app/MindWork AI Studio/Components/ProviderSelection.razor.cs @@ -11,9 +11,9 @@ public partial class ProviderSelection : MSGComponentBase { [CascadingParameter] public AssistantBase? AssistantBase { get; set; } - + [Parameter] - public AIStudio.Settings.Provider ProviderSettings { get; set; } + public AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE; [Parameter] public EventCallback ProviderSettingsChanged { get; set; } @@ -32,7 +32,8 @@ public partial class ProviderSelection : MSGComponentBase { var minimumLevel = this.SettingsManager.GetMinimumConfidenceLevel(this.AssistantBase?.Component ?? Tools.Components.NONE); foreach (var provider in this.SettingsManager.ConfigurationData.Providers) - if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel) - yield return provider; + if (provider.UsedLLMProvider != LLMProviders.NONE) + if (provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level >= minimumLevel) + yield return provider; } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/ReadWebContent.razor.cs b/app/MindWork AI Studio/Components/ReadWebContent.razor.cs index 455c87dd..dc122693 100644 --- a/app/MindWork AI Studio/Components/ReadWebContent.razor.cs +++ b/app/MindWork AI Studio/Components/ReadWebContent.razor.cs @@ -20,7 +20,7 @@ public partial class ReadWebContent : MSGComponentBase public EventCallback ContentChanged { get; set; } [Parameter] - public AIStudio.Settings.Provider ProviderSettings { get; set; } + public AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE; [Parameter] public bool AgentIsRunning { get; set; } @@ -43,7 +43,7 @@ public partial class ReadWebContent : MSGComponentBase private bool urlIsValid; private bool isProviderValid; - private AIStudio.Settings.Provider providerSettings; + private AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE; #region Overrides of ComponentBase @@ -86,7 +86,7 @@ public partial class ReadWebContent : MSGComponentBase this.StateHasChanged(); markdown = this.HTMLParser.ParseToMarkdown(html); - if (this.useContentCleanerAgent) + if (this.useContentCleanerAgent && this.providerSettings != AIStudio.Settings.Provider.NONE) { this.AgentTextContentCleaner.ProviderSettings = this.providerSettings; var additionalData = new Dictionary @@ -149,7 +149,7 @@ public partial class ReadWebContent : MSGComponentBase private string? ValidateProvider(bool shouldUseAgent) { - if(shouldUseAgent && this.providerSettings == default) + if(shouldUseAgent && this.providerSettings == AIStudio.Settings.Provider.NONE) { this.isProviderValid = false; return T("Please select a provider to use the cleanup agent."); diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor.cs b/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor.cs index 608dec29..a2e650da 100644 --- a/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor.cs +++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor.cs @@ -54,6 +54,9 @@ public partial class SettingsPanelProviders : SettingsPanelBase [SuppressMessage("Usage", "MWAIS0001:Direct access to `Providers` is not allowed")] private async Task EditLLMProvider(AIStudio.Settings.Provider provider) { + if(provider == AIStudio.Settings.Provider.NONE) + return; + if (provider.IsEnterpriseConfiguration) return; diff --git a/app/MindWork AI Studio/Pages/Chat.razor.cs b/app/MindWork AI Studio/Pages/Chat.razor.cs index deeda47f..420828c6 100644 --- a/app/MindWork AI Studio/Pages/Chat.razor.cs +++ b/app/MindWork AI Studio/Pages/Chat.razor.cs @@ -21,7 +21,7 @@ public partial class Chat : MSGComponentBase private IDialogService DialogService { get; init; } = null!; private ChatThread? chatThread; - private AIStudio.Settings.Provider providerSettings; + private AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE; private bool workspaceOverlayVisible; private string currentWorkspaceName = string.Empty; private Workspaces? workspaces; diff --git a/app/MindWork AI Studio/Pages/Writer.razor.cs b/app/MindWork AI Studio/Pages/Writer.razor.cs index 56fd0c5d..8bd80016 100644 --- a/app/MindWork AI Studio/Pages/Writer.razor.cs +++ b/app/MindWork AI Studio/Pages/Writer.razor.cs @@ -18,7 +18,7 @@ public partial class Writer : MSGComponentBase private readonly Timer typeTimer = new(TimeSpan.FromMilliseconds(1_500)); private MudTextField textField = null!; - private AIStudio.Settings.Provider providerSettings; + private AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE; private ChatThread? chatThread; private bool isStreaming; private string userInput = string.Empty; diff --git a/app/MindWork AI Studio/Settings/Provider.cs b/app/MindWork AI Studio/Settings/Provider.cs index b7826b2d..f6da48ab 100644 --- a/app/MindWork AI Studio/Settings/Provider.cs +++ b/app/MindWork AI Studio/Settings/Provider.cs @@ -18,7 +18,7 @@ namespace AIStudio.Settings; /// Whether the provider is self-hosted. /// The hostname of the provider. Useful for self-hosted providers. /// The LLM model to use for chat. -public readonly record struct Provider( +public record Provider( uint Num, string Id, string InstanceName, @@ -31,6 +31,21 @@ public readonly record struct Provider( Host Host = Host.NONE, HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE) : ISecretId, IConfigurationObject { + + public static readonly Provider NONE = new(); + + public Provider() : this( + 0, + Guid.Empty.ToString(), + string.Empty, + LLMProviders.NONE, + default, + false, + false, + Guid.Empty) + { + } + #region Overrides of ValueType /// diff --git a/app/MindWork AI Studio/Settings/SettingsManager.cs b/app/MindWork AI Studio/Settings/SettingsManager.cs index 7cad25a2..54e6f1b3 100644 --- a/app/MindWork AI Studio/Settings/SettingsManager.cs +++ b/app/MindWork AI Studio/Settings/SettingsManager.cs @@ -215,18 +215,18 @@ public sealed class SettingsManager return this.ConfigurationData.Providers[0]; // Is there a current provider with a sufficiently high confidence level? - Provider currentProvider = default; + var currentProvider = Provider.NONE; if (currentProviderId is not null && !string.IsNullOrWhiteSpace(currentProviderId)) { var currentProviderProbe = this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == currentProviderId); - if (currentProviderProbe.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel) + if (currentProviderProbe is not null && currentProviderProbe.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel) currentProvider = currentProviderProbe; } // Is there a component-preselected provider with a sufficiently high confidence level? - Provider preselectedProvider = default; + var preselectedProvider = Provider.NONE; var preselectedProviderProbe = component.PreselectedProvider(this); - if(preselectedProviderProbe != default && preselectedProviderProbe.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel) + if(preselectedProviderProbe != Provider.NONE && preselectedProviderProbe.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel) preselectedProvider = preselectedProviderProbe; // @@ -234,14 +234,14 @@ public sealed class SettingsManager // and the preselected provider is available and has a confidence level // that is high enough. // - if(usePreselectionBeforeCurrentProvider && preselectedProvider != default) + if(usePreselectionBeforeCurrentProvider && preselectedProvider != Provider.NONE) return preselectedProvider; // // Case: The current provider is available and has a confidence level that is // high enough. // - if(currentProvider != default) + if(currentProvider != Provider.NONE) return currentProvider; // @@ -250,11 +250,11 @@ public sealed class SettingsManager // level that is high enough. The preselected provider is available and // has a confidence level that is high enough. // - if(preselectedProvider != default) + if(preselectedProvider != Provider.NONE) return preselectedProvider; // When there is an app-wide preselected provider, and it has a confidence level that is high enough, we return it: - return this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedProvider && x.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel); + return this.ConfigurationData.Providers.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedProvider && x.UsedLLMProvider.GetConfidence(this).Level >= minimumLevel) ?? Provider.NONE; } public Profile GetPreselectedProfile(Tools.Components component) diff --git a/app/MindWork AI Studio/Tools/ComponentsExtensions.cs b/app/MindWork AI Studio/Tools/ComponentsExtensions.cs index 18ac4f41..f6552145 100644 --- a/app/MindWork AI Studio/Tools/ComponentsExtensions.cs +++ b/app/MindWork AI Studio/Tools/ComponentsExtensions.cs @@ -113,7 +113,7 @@ public static class ComponentsExtensions Components.AGENT_DATA_SOURCE_SELECTION => settingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.AgentDataSourceSelection.PreselectedAgentProvider) : default, Components.AGENT_RETRIEVAL_CONTEXT_VALIDATION => settingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions ? settingsManager.ConfigurationData.Providers.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectedAgentProvider) : default, - _ => default, + _ => Settings.Provider.NONE, }; public static Profile PreselectedProfile(this Components component, SettingsManager settingsManager) => component switch diff --git a/app/MindWork AI Studio/Tools/Services/DataSourceService.cs b/app/MindWork AI Studio/Tools/Services/DataSourceService.cs index f545aca5..06804474 100644 --- a/app/MindWork AI Studio/Tools/Services/DataSourceService.cs +++ b/app/MindWork AI Studio/Tools/Services/DataSourceService.cs @@ -37,7 +37,7 @@ public sealed class DataSourceService // does not mean anything. We cannot filter the data sources by any means. // We return an empty list. Better safe than sorry. // - if (selectedLLMProvider == default) + if (selectedLLMProvider == Settings.Provider.NONE) { this.logger.LogWarning("The selected LLM provider is not set. We cannot filter the data sources by any means."); return new([], []);