mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-10 01:53:47 +00:00
Multiple Profiles can now be selected
This commit is contained in:
1 parent
d043fbc8f0
commit
cefc5464d5
89 files changed
+1360
-503
No files matched your search
@@ -123,7 +123,7 @@
|
||||
|
||||
<MudDivider Vertical="true" Style="height: 24px; align-self: center;"/>
|
||||
|
||||
<ProfileSelection MarginLeft="" CurrentProfile="@this.currentProfile" CurrentProfileChanged="@this.ProfileWasChanged" Disabled="@(!this.currentChatTemplate.AllowProfileUsage)" DisabledText="@T("Profile usage is disabled according to your chat template settings.")"/>
|
||||
<ProfileSelection MarginLeft="" SelectedProfileIds="@this.currentProfileIds" SelectedProfileIdsChanged="@this.ProfilesWereChanged" Disabled="@(!this.currentChatTemplate.AllowProfileUsage)" DisabledText="@T("Profile usage is disabled according to your chat template settings.")"/>
|
||||
|
||||
@if (this.SettingsManager.AreToolsEnabled())
|
||||
{
|
||||
|
||||
@@ -70,7 +70,7 @@ public partial class ChatComponent : MSGComponentBase
|
||||
private DataSourceSelection? dataSourceSelectionComponent;
|
||||
private DataSourceOptions earlyDataSourceOptions = new();
|
||||
private DataSourceOptions lastAppliedStandardDataSourceOptions = new();
|
||||
private Profile currentProfile = Profile.NO_PROFILE;
|
||||
private HashSet<string> currentProfileIds = [];
|
||||
private ChatTemplate currentChatTemplate = ChatTemplate.NO_CHAT_TEMPLATE;
|
||||
private bool hasUnsavedChanges;
|
||||
private bool mustScrollToBottomAfterRender;
|
||||
@@ -127,7 +127,7 @@ public partial class ChatComponent : MSGComponentBase
|
||||
USER_INPUT_ATTRIBUTES["id"] = CHAT_INPUT_ID;
|
||||
|
||||
// Get the preselected profile:
|
||||
this.currentProfile = this.SettingsManager.GetPreselectedProfile(Tools.Components.CHAT);
|
||||
this.currentProfileIds = this.SettingsManager.GetPreselectedProfiles(Tools.Components.CHAT).Select(profile => profile.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Get the preselected chat template:
|
||||
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(Tools.Components.CHAT);
|
||||
@@ -586,15 +586,15 @@ public partial class ChatComponent : MSGComponentBase
|
||||
return threadName;
|
||||
}
|
||||
|
||||
private async Task ProfileWasChanged(Profile profile)
|
||||
private async Task ProfilesWereChanged(HashSet<string> profileIds)
|
||||
{
|
||||
this.currentProfile = this.SettingsManager.GetProfileById(profile.Id);
|
||||
this.currentProfileIds = this.SettingsManager.ResolveProfiles(profileIds).Select(profile => profile.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
if(this.ChatThread is null)
|
||||
return;
|
||||
|
||||
this.ChatThread = this.ChatThread with
|
||||
{
|
||||
SelectedProfile = this.currentProfile.Id,
|
||||
SelectedProfileIds = [..this.currentProfileIds],
|
||||
};
|
||||
|
||||
await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
|
||||
@@ -617,7 +617,7 @@ public partial class ChatComponent : MSGComponentBase
|
||||
|
||||
private void RefreshCurrentProfileAndChatTemplate()
|
||||
{
|
||||
this.currentProfile = this.SettingsManager.GetProfileById(this.currentProfile.Id);
|
||||
this.currentProfileIds = this.SettingsManager.ResolveProfiles(this.currentProfileIds).Select(profile => profile.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
this.currentChatTemplate = this.SettingsManager.GetChatTemplateById(this.currentChatTemplate.Id);
|
||||
}
|
||||
|
||||
@@ -632,14 +632,12 @@ public partial class ChatComponent : MSGComponentBase
|
||||
|
||||
if (this.ChatThread is null)
|
||||
{
|
||||
this.currentProfile = this.SettingsManager.GetPreselectedProfile(Tools.Components.CHAT);
|
||||
this.currentProfileIds = this.SettingsManager.GetPreselectedProfiles(Tools.Components.CHAT).Select(profile => profile.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(Tools.Components.CHAT);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.currentProfile = string.IsNullOrWhiteSpace(this.ChatThread.SelectedProfile)
|
||||
? this.SettingsManager.GetProfileById(this.currentProfile.Id)
|
||||
: this.SettingsManager.GetProfileById(this.ChatThread.SelectedProfile);
|
||||
this.currentProfileIds = this.SettingsManager.ResolveProfiles(this.ChatThread.SelectedProfileIds).Select(profile => profile.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
this.currentChatTemplate = string.IsNullOrWhiteSpace(this.ChatThread.SelectedChatTemplate)
|
||||
? this.SettingsManager.GetChatTemplateById(this.currentChatTemplate.Id)
|
||||
@@ -761,7 +759,7 @@ public partial class ChatComponent : MSGComponentBase
|
||||
{
|
||||
IncludeDateTime = true,
|
||||
SelectedProvider = this.Provider.Id,
|
||||
SelectedProfile = this.currentProfile.Id,
|
||||
SelectedProfileIds = [..this.currentProfileIds],
|
||||
SelectedChatTemplate = this.currentChatTemplate.Id,
|
||||
SelectedToolIds = [..this.selectedToolIds],
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
@@ -806,7 +804,7 @@ public partial class ChatComponent : MSGComponentBase
|
||||
{
|
||||
IncludeDateTime = true,
|
||||
SelectedProvider = this.Provider.Id,
|
||||
SelectedProfile = this.currentProfile.Id,
|
||||
SelectedProfileIds = [..this.currentProfileIds],
|
||||
SelectedChatTemplate = this.currentChatTemplate.Id,
|
||||
SelectedToolIds = [..this.selectedToolIds],
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
@@ -828,7 +826,7 @@ public partial class ChatComponent : MSGComponentBase
|
||||
|
||||
// Update provider, profile and chat template:
|
||||
this.ChatThread.SelectedProvider = this.Provider.Id;
|
||||
this.ChatThread.SelectedProfile = this.currentProfile.Id;
|
||||
this.ChatThread.SelectedProfileIds = [..this.currentProfileIds];
|
||||
|
||||
//
|
||||
// Remark: We do not update the chat template here
|
||||
@@ -1073,7 +1071,7 @@ public partial class ChatComponent : MSGComponentBase
|
||||
{
|
||||
IncludeDateTime = true,
|
||||
SelectedProvider = this.Provider.Id,
|
||||
SelectedProfile = this.currentProfile.Id,
|
||||
SelectedProfileIds = [..this.currentProfileIds],
|
||||
SelectedChatTemplate = this.currentChatTemplate.Id,
|
||||
SelectedToolIds = [..this.selectedToolIds],
|
||||
SystemPrompt = SystemPrompts.DEFAULT,
|
||||
@@ -1201,7 +1199,7 @@ public partial class ChatComponent : MSGComponentBase
|
||||
private async Task SelectProviderWhenLoadingChat()
|
||||
{
|
||||
var chatProvider = this.ChatThread?.SelectedProvider;
|
||||
var chatProfile = this.ChatThread?.SelectedProfile;
|
||||
var chatProfileIds = this.ChatThread?.SelectedProfileIds;
|
||||
var chatChatTemplate = this.ChatThread?.SelectedChatTemplate;
|
||||
|
||||
this.Provider = this.SettingsManager.GetChatProviderForLoadedChat(chatProvider);
|
||||
@@ -1209,8 +1207,8 @@ public partial class ChatComponent : MSGComponentBase
|
||||
await this.ProviderChanged.InvokeAsync(this.Provider);
|
||||
|
||||
// Try to select the profile:
|
||||
if (!string.IsNullOrWhiteSpace(chatProfile))
|
||||
this.currentProfile = this.SettingsManager.GetProfileById(chatProfile);
|
||||
if (chatProfileIds is not null)
|
||||
this.currentProfileIds = this.SettingsManager.ResolveProfiles(chatProfileIds).Select(profile => profile.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Try to select the chat template:
|
||||
if (!string.IsNullOrWhiteSpace(chatChatTemplate))
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
MultiSelectionTextFunc="@this.GetMultiSelectionText"
|
||||
SelectedValues="@this.SelectedValues()"
|
||||
Strict="@true"
|
||||
MultiSelectionComponent="MultiSelectionComponent.CheckBox"
|
||||
Disabled="@this.IsDisabled"
|
||||
Margin="Margin.Dense"
|
||||
Class="rounded-lg"
|
||||
@@ -23,13 +24,24 @@
|
||||
<MudTooltip Text="@this.LockedTooltip()" Arrow="true" Placement="Placement.Right" RootStyle="display:inline-flex;">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Lock" Color="Color.Error" Size="Size.Small" Class="mr-1"/>
|
||||
</MudTooltip>
|
||||
@data.Name
|
||||
@if (this.ItemTemplate is null)
|
||||
{
|
||||
@data.Name
|
||||
}
|
||||
else
|
||||
{
|
||||
@this.ItemTemplate(data)
|
||||
}
|
||||
</MudStack>
|
||||
}
|
||||
else if (this.ItemTemplate is not null)
|
||||
{
|
||||
@this.ItemTemplate(data)
|
||||
}
|
||||
else
|
||||
{
|
||||
@data.Name
|
||||
}
|
||||
</MudSelectItemExtended>
|
||||
}
|
||||
</MudSelectExtended>
|
||||
</MudSelectExtended>
|
||||
@@ -40,6 +40,15 @@ public partial class ConfigurationMultiSelect<TData> : ConfigurationBaseCore
|
||||
[Parameter]
|
||||
public Func<TData, bool> IsItemLocked { get; set; } = _ => false;
|
||||
|
||||
/// <summary>
|
||||
/// Optional template used to render an item in the list.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public RenderFragment<ConfigurationSelectData<TData>>? ItemTemplate { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<List<TData?>?, string>? MultiSelectionTextFunc { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? EmptySelectionText { get; set; }
|
||||
|
||||
@@ -76,6 +85,9 @@ public partial class ConfigurationMultiSelect<TData> : ConfigurationBaseCore
|
||||
|
||||
private string GetMultiSelectionText(List<TData?>? selectedValues)
|
||||
{
|
||||
if (this.MultiSelectionTextFunc is not null)
|
||||
return this.MultiSelectionTextFunc(selectedValues);
|
||||
|
||||
if(selectedValues is null || selectedValues.Count == 0)
|
||||
return this.EmptySelectionText ?? T("No items selected.");
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@using AIStudio.Settings
|
||||
@inherits MSGComponentBase
|
||||
|
||||
@if (this.availableWorkspaces.Count > 0)
|
||||
@@ -20,14 +21,20 @@
|
||||
</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
<MudSelect T="string" Value="@this.ProfileId" ValueChanged="@this.SetProfileId" Label="@T("Chat profile")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3 rounded-lg" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person">
|
||||
<MudSelectItem T="string" Value="@string.Empty">@T("Use chat default")</MudSelectItem>
|
||||
<MudSelectItem T="string" Value="@Guid.Empty.ToString()">@T("Use no profile")</MudSelectItem>
|
||||
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles)
|
||||
{
|
||||
<MudSelectItem T="string" Value="@profile.Id">@profile.GetSafeName()</MudSelectItem>
|
||||
}
|
||||
<MudSelect T="ProfilePreselectionMode" Value="@this.SelectedProfileMode" ValueChanged="@this.SetProfileMode" Label="@T("Chat profiles")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3 rounded-lg" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person">
|
||||
<MudSelectItem Value="@ProfilePreselectionMode.USE_APP_DEFAULT">@T("Use chat defaults")</MudSelectItem>
|
||||
<MudSelectItem Value="@ProfilePreselectionMode.USE_NO_PROFILES">@T("Use no profiles")</MudSelectItem>
|
||||
<MudSelectItem Value="@ProfilePreselectionMode.USE_SPECIFIC_PROFILES">@T("Use a custom profile selection")</MudSelectItem>
|
||||
</MudSelect>
|
||||
@if (this.SelectedProfileMode is ProfilePreselectionMode.USE_SPECIFIC_PROFILES)
|
||||
{
|
||||
<MudSelect T="string" MultiSelection="true" SelectedValues="@(this.ProfileIds ?? [])" SelectedValuesChanged="@this.SetProfileIds" MultiSelectionTextFunc="@this.GetSelectedProfileText" Label="@T("Profiles")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3 rounded-lg" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person">
|
||||
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles)
|
||||
{
|
||||
<MudSelectItem T="string" Value="@profile.Id">@profile.GetSafeName()</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
}
|
||||
<MudSelect T="string" Value="@this.ChatTemplateId" ValueChanged="@this.SetChatTemplateId" Label="@T("Chat template")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3 rounded-lg" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Article">
|
||||
<MudSelectItem T="string" Value="@string.Empty">@T("Use chat default")</MudSelectItem>
|
||||
<MudSelectItem T="string" Value="@Guid.Empty.ToString()">@T("Use no chat template")</MudSelectItem>
|
||||
@@ -43,4 +50,4 @@
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
<ToolSelectionField Component="Components.CHAT" SelectedToolIds="@this.ToolIds" SelectedToolIdsChanged="@this.SetToolIds" Label="@T("Tools (Optional)")" Help="@T("These tools are preselected when the chat opens. Users can change the selection in the chat, and every tool has to meet the confidence requirements of the provider in use.")"/>
|
||||
<ToolSelectionField Component="Components.CHAT" SelectedToolIds="@this.ToolIds" SelectedToolIdsChanged="@this.SetToolIds" Label="@T("Tools (Optional)")" Help="@T("These tools are preselected when the chat opens. Users can change the selection in the chat, and every tool has to meet the confidence requirements of the provider in use.")"/>
|
||||
@@ -1,3 +1,5 @@
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
@@ -33,14 +35,13 @@ public partial class DirectChatLauncherForm : MSGComponentBase
|
||||
public EventCallback<string> ProviderIdChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The profile ID for the chat, an empty GUID for explicitly no profile, or an empty string to
|
||||
/// use the chat default.
|
||||
/// The exact profile IDs for the chat, an empty set for no profiles, or null for chat defaults.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string ProfileId { get; set; } = string.Empty;
|
||||
public HashSet<string>? ProfileIds { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<string> ProfileIdChanged { get; set; }
|
||||
public EventCallback<HashSet<string>?> ProfileIdsChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The chat template ID, an empty GUID for explicitly no template, or an empty string to use
|
||||
@@ -82,6 +83,7 @@ public partial class DirectChatLauncherForm : MSGComponentBase
|
||||
public Func<string, string?>? ValidateWorkspaceName { get; set; }
|
||||
|
||||
private IReadOnlyList<WorkspaceTreeWorkspace> availableWorkspaces = [];
|
||||
private ProfilePreselectionMode? profileModeOverride;
|
||||
|
||||
private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();
|
||||
|
||||
@@ -124,10 +126,30 @@ public partial class DirectChatLauncherForm : MSGComponentBase
|
||||
await this.ProviderIdChanged.InvokeAsync(providerId);
|
||||
}
|
||||
|
||||
private async Task SetProfileId(string profileId)
|
||||
private ProfilePreselectionMode SelectedProfileMode => this.profileModeOverride ?? ProfilePreselection.FromStoredValue(this.ProfileIds).Mode;
|
||||
|
||||
private async Task SetProfileMode(ProfilePreselectionMode mode)
|
||||
{
|
||||
this.ProfileId = profileId;
|
||||
await this.ProfileIdChanged.InvokeAsync(profileId);
|
||||
this.profileModeOverride = mode;
|
||||
HashSet<string>? profileIds = mode switch
|
||||
{
|
||||
ProfilePreselectionMode.USE_APP_DEFAULT => null,
|
||||
ProfilePreselectionMode.USE_NO_PROFILES => [],
|
||||
ProfilePreselectionMode.USE_SPECIFIC_PROFILES => this.ProfileIds is { Count: > 0 } ? [..this.ProfileIds] : [],
|
||||
_ => null,
|
||||
};
|
||||
|
||||
this.ProfileIds = profileIds;
|
||||
await this.ProfileIdsChanged.InvokeAsync(profileIds);
|
||||
}
|
||||
|
||||
private async Task SetProfileIds(IEnumerable<string?>? profileIds)
|
||||
{
|
||||
var selection = profileIds is null
|
||||
? []
|
||||
: profileIds.Where(profileId => !string.IsNullOrWhiteSpace(profileId)).Select(profileId => profileId!).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
this.ProfileIds = selection;
|
||||
await this.ProfileIdsChanged.InvokeAsync(selection);
|
||||
}
|
||||
|
||||
private async Task SetChatTemplateId(string chatTemplateId)
|
||||
@@ -161,4 +183,15 @@ public partial class DirectChatLauncherForm : MSGComponentBase
|
||||
|
||||
return string.Format(T("{0} data source(s) selected"), selectedValues.Count);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetSelectedProfileText(List<string?>? selectedValues)
|
||||
{
|
||||
var count = selectedValues?.Count ?? 0;
|
||||
return count switch
|
||||
{
|
||||
0 => T("No profiles selected"),
|
||||
1 => T("1 profile"),
|
||||
_ => string.Format(T("{0} profiles"), count),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,10 @@
|
||||
@inherits MSGComponentBase
|
||||
|
||||
<MudStack Row="true" AlignItems="AlignItems.Baseline" StretchItems="StretchItems.Start" Class="mb-3" Wrap="Wrap.NoWrap">
|
||||
<MudSelect T="Profile" Strict="@true" Disabled="@this.Disabled" Value="@this.Profile" ValueChanged="@this.SelectionChanged" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person4" Margin="Margin.Dense" Label="@T("Select one of your profiles")" Variant="Variant.Outlined" Class="mb-3" Validation="@this.Validation">
|
||||
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles.GetAllProfiles())
|
||||
<MudSelect T="string" MultiSelection="@true" Strict="@true" Disabled="@this.Disabled" SelectedValues="@this.ProfileIds" SelectedValuesChanged="@this.SelectionChanged" MultiSelectionTextFunc="@this.GetSelectionText" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person4" Margin="Margin.Dense" Label="@T("Select your profiles")" Variant="Variant.Outlined" Class="mb-3" Validation="@this.ValidateSelection">
|
||||
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles)
|
||||
{
|
||||
<MudSelectItem Value="profile">
|
||||
<MudSelectItem T="string" Value="@profile.Id">
|
||||
@profile.GetSafeName()
|
||||
</MudSelectItem>
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@ namespace AIStudio.Components;
|
||||
public partial class ProfileFormSelection : MSGComponentBase
|
||||
{
|
||||
[Parameter]
|
||||
public Profile Profile { get; set; } = Profile.NO_PROFILE;
|
||||
public HashSet<string> ProfileIds { get; set; } = [];
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<Profile> ProfileChanged { get; set; }
|
||||
public EventCallback<HashSet<string>> ProfileIdsChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<Profile, string?> Validation { get; set; } = _ => null;
|
||||
public Func<HashSet<string>, string?> Validation { get; set; } = _ => null;
|
||||
|
||||
[Parameter]
|
||||
public bool Disabled { get; set; }
|
||||
@@ -24,10 +24,29 @@ public partial class ProfileFormSelection : MSGComponentBase
|
||||
[Inject]
|
||||
public IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
private async Task SelectionChanged(Profile profile)
|
||||
private async Task SelectionChanged(IEnumerable<string?>? profileIds)
|
||||
{
|
||||
this.Profile = profile;
|
||||
await this.ProfileChanged.InvokeAsync(profile);
|
||||
var selection = profileIds is null
|
||||
? []
|
||||
: profileIds.Where(profileId => !string.IsNullOrWhiteSpace(profileId)).Select(profileId => profileId!).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
this.ProfileIds = selection;
|
||||
await this.ProfileIdsChanged.InvokeAsync(selection);
|
||||
}
|
||||
|
||||
private string? ValidateSelection(IEnumerable<string?>? profileIds) => this.Validation(
|
||||
profileIds is null
|
||||
? []
|
||||
: profileIds.Where(profileId => !string.IsNullOrWhiteSpace(profileId)).Select(profileId => profileId!).ToHashSet(StringComparer.OrdinalIgnoreCase));
|
||||
|
||||
private string GetSelectionText(List<string?>? profileIds)
|
||||
{
|
||||
var profiles = this.SettingsManager.ResolveProfiles(profileIds?.Where(profileId => profileId is not null).Select(profileId => profileId!));
|
||||
return profiles.Count switch
|
||||
{
|
||||
0 => T("No profiles selected"),
|
||||
1 => profiles[0].GetSafeName(),
|
||||
_ => string.Format(T("{0} profiles"), profiles.Count),
|
||||
};
|
||||
}
|
||||
|
||||
private async Task OpenSettingsDialog()
|
||||
@@ -35,4 +54,4 @@ public partial class ProfileFormSelection : MSGComponentBase
|
||||
var dialogParameters = new DialogParameters();
|
||||
await this.DialogService.ShowAsync<SettingsDialogProfiles>(T("Open Profile Options"), dialogParameters, DialogOptions.FULLSCREEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
@using AIStudio.Settings
|
||||
@inherits MSGComponentBase
|
||||
|
||||
<ConfigurationSelect TConfig="ProfilePreselectionMode" OptionDescription="@this.OptionDescription" OptionHelp="@this.OptionHelp" Disabled="@this.Disabled" IsLocked="@this.IsLocked" SelectedValue="@(() => this.SelectedMode)" Data="@this.GetModeData()" SelectionUpdateAsync="@this.ModeChanged" />
|
||||
|
||||
@if (this.SelectedMode is ProfilePreselectionMode.USE_SPECIFIC_PROFILES)
|
||||
{
|
||||
<ConfigurationMultiSelect TData="string" OptionDescription="@T("Profiles")" Disabled="@this.Disabled" IsLocked="@this.IsLocked" SelectedValues="@(() => this.SpecificProfileIds)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdateAsync="@this.ProfilesChanged" MultiSelectionTextFunc="@this.GetSelectionText">
|
||||
<ItemTemplate Context="profileData">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||
<MudIcon Icon="@this.ProfileIcon(profileData.Value)" Size="Size.Small" Style="color: var(--mud-palette-action-default);" />
|
||||
<MudText Typo="Typo.body1">@profileData.Name</MudText>
|
||||
</MudStack>
|
||||
</ItemTemplate>
|
||||
</ConfigurationMultiSelect>
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using AIStudio.Settings;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
public partial class ProfilePreselectionConfiguration : MSGComponentBase
|
||||
{
|
||||
private ProfilePreselectionMode? selectedModeOverride;
|
||||
|
||||
[Parameter]
|
||||
public string OptionDescription { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string OptionHelp { get; set; } = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public Func<bool> Disabled { get; set; } = () => false;
|
||||
|
||||
[Parameter]
|
||||
public Func<bool> IsLocked { get; set; } = () => false;
|
||||
|
||||
[Parameter]
|
||||
public Func<HashSet<string>?> SelectedProfileIds { get; set; } = () => null;
|
||||
|
||||
[Parameter]
|
||||
public Action<HashSet<string>?> SelectionUpdate { get; set; } = _ => { };
|
||||
|
||||
[Parameter]
|
||||
public Func<HashSet<string>?, Task> SelectionUpdateAsync { get; set; } = _ => Task.CompletedTask;
|
||||
|
||||
private ProfilePreselectionMode SelectedMode => this.selectedModeOverride ?? ProfilePreselection.FromStoredValue(this.SelectedProfileIds()).Mode;
|
||||
|
||||
private HashSet<string> SpecificProfileIds => this.SelectedProfileIds() ?? [];
|
||||
|
||||
private async Task ModeChanged(ProfilePreselectionMode mode)
|
||||
{
|
||||
this.selectedModeOverride = mode;
|
||||
HashSet<string>? selection = mode switch
|
||||
{
|
||||
ProfilePreselectionMode.USE_APP_DEFAULT => null,
|
||||
ProfilePreselectionMode.USE_NO_PROFILES => [],
|
||||
ProfilePreselectionMode.USE_SPECIFIC_PROFILES => this.SpecificProfileIds.Count > 0 ? [..this.SpecificProfileIds] : [],
|
||||
_ => null,
|
||||
};
|
||||
|
||||
await this.UpdateSelection(selection);
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
}
|
||||
|
||||
private async Task ProfilesChanged(HashSet<string> profileIds) => await this.UpdateSelection(profileIds.ToHashSet(StringComparer.OrdinalIgnoreCase));
|
||||
|
||||
private async Task UpdateSelection(HashSet<string>? selection)
|
||||
{
|
||||
this.SelectionUpdate(selection);
|
||||
await this.SelectionUpdateAsync(selection);
|
||||
}
|
||||
|
||||
private string GetSelectionText(List<string?>? profileIds)
|
||||
{
|
||||
var profiles = this.SettingsManager.ResolveProfiles(profileIds?.OfType<string>());
|
||||
return profiles.Count == 0 ? T("No profiles selected") : string.Join(", ", profiles.Select(profile => profile.GetSafeName()));
|
||||
}
|
||||
|
||||
private IEnumerable<ConfigurationSelectData<ProfilePreselectionMode>> GetModeData()
|
||||
{
|
||||
yield return new(T("Use app default"), ProfilePreselectionMode.USE_APP_DEFAULT);
|
||||
yield return new(T("Use no profiles"), ProfilePreselectionMode.USE_NO_PROFILES);
|
||||
yield return new(T("Use a custom profile selection"), ProfilePreselectionMode.USE_SPECIFIC_PROFILES);
|
||||
}
|
||||
|
||||
private string ProfileIcon(string profileId) => this.SettingsManager.GetProfileById(profileId).IsEnterpriseConfiguration
|
||||
? Icons.Material.Filled.Business
|
||||
: Icons.Material.Filled.Person4;
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
@using AIStudio.Settings
|
||||
@inherits MSGComponentBase
|
||||
<MudTooltip Text="@this.ToolTipText" Placement="Placement.Top">
|
||||
<MudMenu TransformOrigin="@Origin.BottomLeft" AnchorOrigin="Origin.TopLeft" StartIcon="@Icons.Material.Filled.Person4" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@this.CurrentProfile.Name" Variant="Variant.Filled" Color="Color.Default" Class="@this.MarginClass" Disabled="@this.Disabled">
|
||||
<MudMenu TransformOrigin="@Origin.BottomLeft" AnchorOrigin="Origin.TopLeft" StartIcon="@Icons.Material.Filled.Person4" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@this.SelectionLabel" Variant="Variant.Filled" Color="Color.Default" Class="@this.MarginClass" Disabled="@this.Disabled">
|
||||
<ActivatorContent>
|
||||
@if (this.CurrentProfile != Profile.NO_PROFILE)
|
||||
@if (this.SelectedProfiles.Count > 0)
|
||||
{
|
||||
<MudButton IconSize="Size.Large" StartIcon="@Icons.Material.Filled.Person4" IconColor="Color.Default">
|
||||
@this.CurrentProfile.GetSafeName()
|
||||
@this.SelectionLabel
|
||||
</MudButton>
|
||||
}
|
||||
else
|
||||
@@ -17,10 +17,16 @@
|
||||
<ChildContent>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Settings" Label="@T("Manage your profiles")" OnClick="@(async () => await this.OpenSettingsDialog())" />
|
||||
<MudDivider/>
|
||||
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles.GetAllProfiles())
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.PersonOff" AutoClose="false" Disabled="@(this.SelectedProfiles.Count == 0)" OnClick="@this.ClearSelection">
|
||||
@T("Clear all")
|
||||
</MudMenuItem>
|
||||
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles)
|
||||
{
|
||||
<MudMenuItem Icon="@this.ProfileIcon(profile)" OnClick="@(() => this.SelectionChanged(profile))">
|
||||
@profile.GetSafeName()
|
||||
<MudMenuItem Icon="@(this.SelectedProfileIds.Contains(profile.Id) ? Icons.Material.Filled.CheckBox : Icons.Material.Filled.CheckBoxOutlineBlank)" IconColor="Color.Primary" AutoClose="false" Class="mud-list-item-dense" OnClick="@(() => this.SelectionChanged(profile, !this.SelectedProfileIds.Contains(profile.Id)))">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||
<MudIcon Icon="@this.ProfileIcon(profile)" Size="Size.Small" Style="color: var(--mud-palette-action-default);" />
|
||||
<MudText Typo="Typo.body1">@profile.GetSafeName()</MudText>
|
||||
</MudStack>
|
||||
</MudMenuItem>
|
||||
}
|
||||
</ChildContent>
|
||||
|
||||
@@ -12,10 +12,10 @@ public partial class ProfileSelection : MSGComponentBase
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ProfileSelection).Namespace, nameof(ProfileSelection));
|
||||
|
||||
[Parameter]
|
||||
public Profile CurrentProfile { get; set; } = Profile.NO_PROFILE;
|
||||
public HashSet<string> SelectedProfileIds { get; set; } = [];
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<Profile> CurrentProfileChanged { get; set; }
|
||||
public EventCallback<HashSet<string>> SelectedProfileIdsChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string MarginLeft { get; set; } = "ml-3";
|
||||
@@ -32,9 +32,22 @@ public partial class ProfileSelection : MSGComponentBase
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
private readonly string defaultToolTipText = TB("You can switch between your profiles here");
|
||||
private readonly string defaultToolTipText = TB("You can select your profiles here");
|
||||
|
||||
private string ToolTipText => this.Disabled ? this.DisabledText : this.defaultToolTipText;
|
||||
private IReadOnlyList<Profile> SelectedProfiles => this.SettingsManager.ResolveProfiles(this.SelectedProfileIds);
|
||||
|
||||
private string SelectionLabel => this.SelectedProfiles.Count switch
|
||||
{
|
||||
0 => string.Empty,
|
||||
1 => this.SelectedProfiles[0].GetSafeName(),
|
||||
_ => string.Format(TB("{0} profiles"), this.SelectedProfiles.Count),
|
||||
};
|
||||
|
||||
private string ToolTipText => this.Disabled
|
||||
? this.DisabledText
|
||||
: this.SelectedProfiles.Count > 1
|
||||
? string.Join(", ", this.SelectedProfiles.Select(profile => profile.GetSafeName()))
|
||||
: this.defaultToolTipText;
|
||||
|
||||
private string MarginClass => $"{this.MarginLeft} {this.MarginRight}";
|
||||
|
||||
@@ -47,19 +60,31 @@ public partial class ProfileSelection : MSGComponentBase
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private string ProfileIcon(Profile profile)
|
||||
{
|
||||
if (profile.IsEnterpriseConfiguration)
|
||||
return Icons.Material.Filled.Business;
|
||||
|
||||
|
||||
return Icons.Material.Filled.Person4;
|
||||
}
|
||||
|
||||
private async Task SelectionChanged(Profile profile)
|
||||
private async Task SelectionChanged(Profile profile, bool selected)
|
||||
{
|
||||
this.CurrentProfile = profile;
|
||||
await this.CurrentProfileChanged.InvokeAsync(profile);
|
||||
var updatedSelection = new HashSet<string>(this.SelectedProfileIds, StringComparer.OrdinalIgnoreCase);
|
||||
if (selected)
|
||||
updatedSelection.Add(profile.Id);
|
||||
else
|
||||
updatedSelection.Remove(profile.Id);
|
||||
|
||||
this.SelectedProfileIds = updatedSelection;
|
||||
await this.SelectedProfileIdsChanged.InvokeAsync(updatedSelection);
|
||||
}
|
||||
|
||||
private async Task ClearSelection()
|
||||
{
|
||||
this.SelectedProfileIds = [];
|
||||
await this.SelectedProfileIdsChanged.InvokeAsync([]);
|
||||
}
|
||||
|
||||
private async Task OpenSettingsDialog()
|
||||
@@ -79,4 +104,4 @@ public partial class ProfileSelection : MSGComponentBase
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
}
|
||||
|
||||
<ConfigurationProviderSelection Component="Components.APP_SETTINGS" Data="@this.AvailableLLMProvidersFunc()" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProvider = selectedValue)" HelpText="@(() => T("Would you like to set one provider as the default for the entire app? When you configure a different provider for an assistant, it will always take precedence."))" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.PreselectedProvider, out var meta) && meta.IsLocked"/>
|
||||
<ConfigurationSelect OptionDescription="@T("Preselect one of your profiles?")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="@T("Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.PreselectedProfile, out var meta) && meta.IsLocked"/>
|
||||
<ConfigurationMultiSelect OptionDescription="@T("Preselect profiles")" SelectedValues="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfileIds)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfileIds = selectedValue)" OptionHelp="@T("Choose the profiles used by default throughout the app. A component-specific selection replaces this set completely.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.PreselectedProfileIds, out var meta) && meta.IsLocked" EmptySelectionText="@T("No profiles selected.")" SingleSelectionText="@T("You have selected 1 profile.")" MultipleSelectionText="@T("You have selected {0} profiles.")"/>
|
||||
|
||||
@if (PreviewFeatures.PRE_SPEECH_TO_TEXT_2026.IsEnabled(this.SettingsManager))
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user