mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-02 04:07:21 +00:00
194 lines
8.5 KiB
C#
194 lines
8.5 KiB
C#
using AIStudio.Provider;
|
|
using AIStudio.Settings;
|
|
using AIStudio.Tools.ToolCallingSystem;
|
|
|
|
using Microsoft.AspNetCore.Components;
|
|
|
|
namespace AIStudio.Components;
|
|
|
|
public partial class ProviderSelection : MSGComponentBase
|
|
{
|
|
[CascadingParameter]
|
|
public Tools.Components? Component { get; set; }
|
|
|
|
[Parameter]
|
|
public AIStudio.Settings.Provider ProviderSettings { get; set; } = AIStudio.Settings.Provider.NONE;
|
|
|
|
[Parameter]
|
|
public EventCallback<AIStudio.Settings.Provider> ProviderSettingsChanged { get; set; }
|
|
|
|
[Parameter]
|
|
public Func<AIStudio.Settings.Provider, string?> ValidateProvider { get; set; } = _ => null;
|
|
|
|
/// <summary>
|
|
/// What this place calls the thing being picked, when "Provider" is not the word it uses.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Some places have the user pick a provider in order to set it up, and there the word is right.
|
|
/// Others have them pick one to get a job done, and speak of the model throughout. A field
|
|
/// labelled "Provider" in the middle of such a text reads like a second, different choice.
|
|
/// </remarks>
|
|
[Parameter]
|
|
public string? Label { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets whether provider selection is disabled.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// A choice which reaches a disabled selection anyway is turned down. Keeping the selected provider
|
|
/// up to date after its settings changed is no choice, so that goes on.
|
|
/// </remarks>
|
|
[Parameter]
|
|
public bool Disabled { get; set; }
|
|
|
|
[Parameter]
|
|
public ConfidenceLevel ExplicitMinimumConfidence { get; set; } = ConfidenceLevel.UNKNOWN;
|
|
|
|
[Inject]
|
|
private ILogger<ProviderSelection> Logger { get; init; } = null!;
|
|
|
|
#region Overrides of ComponentBase
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED ]);
|
|
await base.OnInitializedAsync();
|
|
}
|
|
|
|
#endregion
|
|
|
|
private async Task SelectionChanged(AIStudio.Settings.Provider? provider)
|
|
{
|
|
if (this.Disabled)
|
|
return;
|
|
|
|
this.ProviderSettings = provider ?? AIStudio.Settings.Provider.NONE;
|
|
await this.ProviderSettingsChanged.InvokeAsync(this.ProviderSettings);
|
|
}
|
|
|
|
private IEnumerable<ProviderSelectionItem> GetAvailableProviderSelectionItems()
|
|
{
|
|
foreach (var provider in this.GetAvailableProviders())
|
|
yield return new(provider, this.GetCapabilityIcons(provider));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Says why there is nothing to choose from, or nothing at all when that is not the user's doing.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// An empty list has three causes the user can act on, and they lead to different places in the
|
|
/// settings: there is no provider yet, the organization prohibited all of them, or none of the
|
|
/// configured ones reaches the confidence this component asks for. Naming the wrong one sends
|
|
/// the user looking in the wrong place -- a first start has nobody to blame for a confidence
|
|
/// level it never set. A missing or invalid component is another case and none of those: it is
|
|
/// a defect, it was logged as one, and any explanation offered to the user here would be a guess.
|
|
/// </remarks>
|
|
private string? GetEmptySelectionHint()
|
|
{
|
|
if (this.Component is null or Tools.Components.NONE)
|
|
return null;
|
|
|
|
var configuredProviders = this.SettingsManager.GetAllProviders().Where(x => x.UsedLLMProvider is not LLMProviders.NONE).ToList();
|
|
if (configuredProviders.Count == 0)
|
|
return this.T("No LLM providers are configured yet. Add a provider in the app settings.");
|
|
|
|
if (configuredProviders.All(x => this.SettingsManager.IsProviderProhibited(x.UsedLLMProvider)))
|
|
return this.T("The use of all your LLM providers has been prohibited by your organization. Add a different provider in the app settings.");
|
|
|
|
return this.T("No LLM providers meet the confidence requirements. Configure an eligible provider in the app settings.");
|
|
}
|
|
|
|
private IReadOnlyList<CapabilityIcon> GetCapabilityIcons(AIStudio.Settings.Provider provider)
|
|
{
|
|
var profile = provider.GetModelProfile();
|
|
List<CapabilityIcon> capabilityIcons = [];
|
|
|
|
if (profile.Has(Capability.AUDIO_INPUT))
|
|
capabilityIcons.Add(new(Icons.Material.Filled.GraphicEq, this.T("Audio input possible")));
|
|
|
|
if (profile.HasAny(Capability.SINGLE_IMAGE_INPUT | Capability.MULTIPLE_IMAGE_INPUT))
|
|
capabilityIcons.Add(new(Icons.Material.Filled.Image, this.T("Image input possible")));
|
|
|
|
if (profile.Has(Capability.SPEECH_INPUT))
|
|
capabilityIcons.Add(new(Icons.Material.Filled.Mic, this.T("Speech input possible")));
|
|
|
|
// The same check which decides whether a request offers tools, Semantic Search among them:
|
|
if (provider.GetToolCallingAvailability().IsAvailable)
|
|
capabilityIcons.Add(new(Icons.Material.Filled.Build, this.T("Tool calling possible")));
|
|
|
|
var reasoningIndicatorState = provider.GetReasoningIndicatorState();
|
|
if (reasoningIndicatorState is not ReasoningIndicatorState.NONE)
|
|
capabilityIcons.Add(new(Icons.Material.Filled.Psychology, this.GetReasoningTooltip(reasoningIndicatorState)));
|
|
|
|
return capabilityIcons;
|
|
}
|
|
|
|
private string GetReasoningTooltip(ReasoningIndicatorState reasoningIndicatorState) => reasoningIndicatorState switch
|
|
{
|
|
ReasoningIndicatorState.DEFAULT_ON => this.T("Uses reasoning (thinking) by default"),
|
|
ReasoningIndicatorState.CONFIGURED => this.T("Uses reasoning (thinking) configured by settings"),
|
|
_ => this.T("Uses reasoning (thinking)"),
|
|
};
|
|
|
|
private IEnumerable<AIStudio.Settings.Provider> GetAvailableProviders()
|
|
{
|
|
switch (this.Component)
|
|
{
|
|
case null:
|
|
this.Logger.LogError("Component is null! Cannot filter providers based on component settings. Missed CascadingParameter?");
|
|
yield break;
|
|
|
|
case Tools.Components.NONE:
|
|
this.Logger.LogError("Component is NONE! Cannot filter providers based on component settings. Used wrong component?");
|
|
yield break;
|
|
|
|
case { } component:
|
|
|
|
// Filter providers based on the minimum confidence level of this component, the
|
|
// enforced global minimum, and the explicit minimum level when it is higher:
|
|
foreach (var provider in this.SettingsManager.GetConfidentProviders(component, this.ExplicitMinimumConfidence))
|
|
yield return provider;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#region Overrides of MSGComponentBase
|
|
|
|
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
|
|
{
|
|
if (triggeredEvent is Event.CONFIGURATION_CHANGED or Event.PLUGINS_RELOADED)
|
|
{
|
|
//
|
|
// We hold a copy of the provider record, which is a snapshot taken when it was selected.
|
|
// Once the user edits that provider, our copy is stale and would keep showing the old
|
|
// name and the old icon, so we resolve it again and hand the fresh one to our parent:
|
|
//
|
|
var updatedProvider = this.SettingsManager.GetProviderById(this.ProviderSettings.Id);
|
|
if (updatedProvider != AIStudio.Settings.Provider.NONE && updatedProvider != this.ProviderSettings)
|
|
{
|
|
this.ProviderSettings = updatedProvider;
|
|
await this.ProviderSettingsChanged.InvokeAsync(updatedProvider);
|
|
}
|
|
|
|
//
|
|
// A configuration may also take our provider out of reach, e.g., when the organization
|
|
// prohibits it or raises the minimum confidence. The list already leaves it out, so the
|
|
// selection must not keep showing it either:
|
|
//
|
|
if (this.Component is not (null or Tools.Components.NONE) && this.ProviderSettings != AIStudio.Settings.Provider.NONE && !this.GetAvailableProviders().Contains(this.ProviderSettings))
|
|
{
|
|
this.ProviderSettings = AIStudio.Settings.Provider.NONE;
|
|
await this.ProviderSettingsChanged.InvokeAsync(AIStudio.Settings.Provider.NONE);
|
|
}
|
|
|
|
this.StateHasChanged();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
private readonly record struct CapabilityIcon(string Icon, string Tooltip);
|
|
|
|
private readonly record struct ProviderSelectionItem(AIStudio.Settings.Provider Provider, IReadOnlyList<CapabilityIcon> CapabilityIcons);
|
|
}
|