Use Provider.NONE as a default value and refactor provider handling logic across components.

This commit is contained in:
Thorsten Sommer 2025-08-19 20:37:45 +02:00
parent 589d62c9ce
commit 1765a4d514
Signed by: tsommer
GPG Key ID: 371BBA77A02C0108
17 changed files with 66 additions and 35 deletions

View File

@ -54,7 +54,7 @@ public abstract class AgentBase(ILogger<AgentBase> 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<ChatThread> ProcessContext(ChatThread chatThread, IDictionary<string, string> additionalData);
@ -103,10 +103,9 @@ public abstract class AgentBase(ILogger<AgentBase> 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<AgentBase> 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);
}
}

View File

@ -86,7 +86,7 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
""";
/// <inheritdoc />
public override Settings.Provider? ProviderSettings { get; set; }
public override Settings.Provider ProviderSettings { get; set; } = Settings.Provider.NONE;
/// <summary>
/// The data source selection agent does not work with context. Use
@ -141,6 +141,11 @@ public sealed class AgentDataSourceSelection (ILogger<AgentDataSourceSelection>
// 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()}).");

View File

@ -71,7 +71,7 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
""";
/// <inheritdoc />
public override Settings.Provider? ProviderSettings { get; set; }
public override Settings.Provider ProviderSettings { get; set; } = Settings.Provider.NONE;
/// <summary>
/// The retrieval context validation agent does not work with context. Use
@ -133,6 +133,11 @@ public sealed class AgentRetrievalContextValidation (ILogger<AgentRetrievalConte
{
// We start with the provider currently selected by the user:
var agentProvider = this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_RETRIEVAL_CONTEXT_VALIDATION, provider.Id, true);
if (agentProvider == Settings.Provider.NONE)
{
logger.LogWarning("No provider is selected for the agent.");
return;
}
// Assign the provider settings to the agent:
logger.LogInformation($"The agent for the retrieval context validation uses the provider '{agentProvider.InstanceName}' ({agentProvider.UsedLLMProvider.ToName()}, confidence={agentProvider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()}).");

View File

@ -11,7 +11,7 @@ public sealed class AgentTextContentCleaner(ILogger<AgentBase> 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;

View File

@ -12,7 +12,7 @@ public interface IAgent
/// <summary>
/// The provider to use for this agent.
/// </summary>
public AIStudio.Settings.Provider? ProviderSettings { get; set; }
public Settings.Provider ProviderSettings { get; set; }
/// <summary>
/// Processes a chat thread (i.e., context) and returns the updated thread.

View File

@ -85,7 +85,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
protected virtual IReadOnlyList<IButtonData> 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<TSettings> : 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);

View File

@ -18,9 +18,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
[Parameter]
public EventCallback<ChatThread?> 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<AIStudio.Settings.Provider> 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;
}

View File

@ -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;
}

View File

@ -11,9 +11,9 @@ public partial class ProviderSelection : MSGComponentBase
{
[CascadingParameter]
public AssistantBase<NoComponent>? 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<AIStudio.Settings.Provider> 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;
}
}

View File

@ -20,7 +20,7 @@ public partial class ReadWebContent : MSGComponentBase
public EventCallback<string> 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<string, string>
@ -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.");

View File

@ -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;

View File

@ -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;

View File

@ -18,7 +18,7 @@ public partial class Writer : MSGComponentBase
private readonly Timer typeTimer = new(TimeSpan.FromMilliseconds(1_500));
private MudTextField<string> 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;

View File

@ -18,7 +18,7 @@ namespace AIStudio.Settings;
/// <param name="IsSelfHosted">Whether the provider is self-hosted.</param>
/// <param name="Hostname">The hostname of the provider. Useful for self-hosted providers.</param>
/// <param name="Model">The LLM model to use for chat.</param>
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
/// <summary>

View File

@ -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)

View File

@ -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

View File

@ -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([], []);