Changes based on code review, changed the profile tooltip, added translation option for chat roles, and added the option to preselect a chat template for chats

This commit is contained in:
Peer Schütt 2025-05-23 15:32:20 +02:00
parent e7b32848e5
commit 10edb1ec54
16 changed files with 71 additions and 56 deletions

View File

@ -89,6 +89,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
protected MudForm? form;
protected bool inputIsValid;
protected Profile currentProfile = Profile.NO_PROFILE;
protected ChatTemplate currentChatTemplate = ChatTemplate.NO_CHATTEMPLATE;
protected ChatThread? chatThread;
protected IContent? lastUserPrompt;
protected CancellationTokenSource? cancellationTokenSource;
@ -115,6 +116,7 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase wher
this.MightPreselectValues();
this.providerSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
this.currentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(this.Component);
}
protected override async Task OnParametersSetAsync()

View File

@ -1,3 +1,5 @@
using AIStudio.Tools.PluginSystem;
namespace AIStudio.Chat;
/// <summary>
@ -19,6 +21,8 @@ public enum ChatRole
/// </summary>
public static class ExtensionsChatRole
{
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ChatRole).Namespace, nameof(ChatRole));
/// <summary>
/// Returns the name of the role.
/// </summary>
@ -26,11 +30,11 @@ public static class ExtensionsChatRole
/// <returns>The name of the role.</returns>
public static string ToName(this ChatRole role) => role switch
{
ChatRole.SYSTEM => "System",
ChatRole.USER => "You",
ChatRole.AI => "AI",
ChatRole.SYSTEM => TB("System"),
ChatRole.USER => TB("You"),
ChatRole.AI => TB("AI"),
_ => "Unknown",
_ => TB("Unknown"),
};
/// <summary>
@ -68,10 +72,10 @@ public static class ExtensionsChatRole
/// <returns>The name of the role.</returns>
public static string ToChatTemplateName(this ChatRole role) => role switch
{
ChatRole.SYSTEM => "System",
ChatRole.USER => "User",
ChatRole.AI => "Assistant",
ChatRole.SYSTEM => TB("System"),
ChatRole.USER => TB("User"),
ChatRole.AI => TB("Assistant"),
_ => "Unknown",
_ => TB("Unknown"),
};
}

View File

@ -112,7 +112,7 @@
</MudTooltip>
}
<ProfileSelection CurrentProfile="@this.currentProfile" CurrentProfileChanged="@this.ProfileWasChanged" Disabled="@(!this.currentChatTemplate.AllowProfileUsage)"/>
<ProfileSelection CurrentProfile="@this.currentProfile" CurrentProfileChanged="@this.ProfileWasChanged" Disabled="@(!this.currentChatTemplate.AllowProfileUsage)" DisabledText="@T("The profile is disabled according to your chat template settings.")"/>
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
{

View File

@ -60,8 +60,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
private string currentWorkspaceName = string.Empty;
private Guid currentWorkspaceId = Guid.Empty;
private CancellationTokenSource? cancellationTokenSource;
private bool disableProfile = false; // TODO
// Unfortunately, we need the input field reference to blur the focus away. Without
// this, we cannot clear the input field.
private MudTextField<string> inputField = null!;
@ -80,7 +79,7 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
this.currentProfile = this.SettingsManager.GetPreselectedProfile(Tools.Components.CHAT);
// Get the preselected chat template:
// this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(Tools.Components.CHAT); // TODO
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(Tools.Components.CHAT);
//
// Check for deferred messages of the kind 'SEND_TO_CHAT',
@ -329,18 +328,9 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
private async Task ChatTemplateWasChanged(ChatTemplate chatTemplate)
{
this.currentChatTemplate = chatTemplate;
this.disableProfile = !chatTemplate.AllowProfileUsage;
if(this.ChatThread is null)
return;
this.ChatThread = this.ChatThread with
{
SelectedChatTemplate = this.currentChatTemplate.Id,
Blocks = this.currentChatTemplate.AdditionalMessages.Select(x => x.DeepClone()).ToList(),
};
// await this.ChatThreadChanged.InvokeAsync(this.ChatThread);
await this.StartNewChat(true, false);
}
@ -817,7 +807,6 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
this.currentChatTemplate = this.SettingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == chatChatTemplate);
if(this.currentChatTemplate == default)
this.currentChatTemplate = ChatTemplate.NO_CHATTEMPLATE;
this.disableProfile = !this.currentChatTemplate.AllowProfileUsage;
}
}

View File

@ -1,6 +1,6 @@
@inherits MSGComponentBase
<MudTooltip Text="@T("You can switch between your profiles here")" 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">
<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">
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles.GetAllProfiles())
{
<MudMenuItem OnClick="() => this.SelectionChanged(profile)">

View File

@ -1,11 +1,13 @@
using AIStudio.Settings;
using AIStudio.Tools.PluginSystem;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Components;
public partial class ProfileSelection : MSGComponentBase
{
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ConfigurationProviderSelection).Namespace, nameof(ConfigurationProviderSelection));
[Parameter]
public Profile CurrentProfile { get; set; } = Profile.NO_PROFILE;
@ -21,6 +23,13 @@ public partial class ProfileSelection : MSGComponentBase
[Parameter]
public bool Disabled { get; set; }
[Parameter]
public string DisabledText { get; set; } = string.Empty;
private readonly string defaultToolTipText = TB("You can switch between your profiles here");
private string ToolTipText => this.Disabled ? this.DisabledText : this.defaultToolTipText;
private string MarginClass => $"{this.MarginLeft} {this.MarginRight}";
private async Task SelectionChanged(Profile profile)

View File

@ -12,6 +12,7 @@
<ConfigurationOption OptionDescription="@T("Preselect chat options?")" LabelOn="@T("Chat options are preselected")" LabelOff="@T("No chat options are preselected")" State="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Chat.PreselectOptions = updatedState)" OptionHelp="@T("When enabled, you can preselect chat options. This is might be useful when you prefer a specific provider.")"/>
<ConfigurationProviderSelection Component="Components.CHAT" Data="@this.AvailableLLMProvidersFunc()" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider = selectedValue)"/>
<ConfigurationSelect OptionDescription="@T("Preselect one of your profiles?")" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedProfile = selectedValue)" OptionHelp="@T("Would you like to set one of your profiles as the default for chats?")"/>
<ConfigurationSelect OptionDescription="@T("Preselect one of your chat templates?")" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedChatTemplate)" Data="@ConfigurationSelectDataFactory.GetChatTemplatesData(this.SettingsManager.ConfigurationData.ChatTemplates)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedChatTemplate = selectedValue)" OptionHelp="@T("Would you like to set one of your chat templates as the default for chats?")"/>
</MudPaper>
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))

View File

@ -30,14 +30,12 @@ public partial class SettingsPanelChatTemplates : SettingsPanelBase
private async Task EditChatTemplate(ChatTemplate chatTemplate)
{
// TODO: additionall messages übergeben
var dialogParameters = new DialogParameters<ChatTemplateDialog>
{
{ x => x.DataNum, chatTemplate.Num },
{ x => x.DataId, chatTemplate.Id },
{ x => x.DataName, chatTemplate.Name },
{ x => x.DataSystemPrompt, chatTemplate.SystemPrompt },
// { x => x.DataActions, chatTemplate.Actions },
{ x => x.IsEditing, true },
{x => x.AdditionalMessages, chatTemplate.AdditionalMessages},
};

View File

@ -66,16 +66,16 @@
@T("Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs.")
</MudJustifiedText>
<MudTable CanCancelEdit="true" Class="mt-3 mb-6" CommitEditTooltip="@T("Commit Changes")" Elevation="10" FixedHeader="true" Items="@AdditionalMessages" Outlined="true" RowEditCancel="@this.ResetItemToOriginalValues" RowEditCommit="@this.ItemHasBeenCommitted" RowEditPreview="@this.BackupItem">
<MudTable CanCancelEdit="true" Class="mt-3 mb-6" CommitEditTooltip="@T("Commit Changes")" Elevation="10" FixedHeader="true" Items="@AdditionalMessages" Outlined="true" RowEditCancel="@this.ResetItemToOriginalValues" RowEditPreview="@this.BackupItem">
<ColGroup>
<col style="width: 16em;" />
<col/>
<col style="width: 16em;" />
</ColGroup>
<HeaderContent>
<MudTh>Role</MudTh>
<MudTh>Entry</MudTh>
<MudTh Style="text-align:center">Actions</MudTh>
<MudTh>@T("Role")</MudTh>
<MudTh>@T("Entry")</MudTh>
<MudTh Style="text-align:center">@T("Actions")</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="@T("Role")">@context.Role.ToChatTemplateName()</MudTd>
@ -97,11 +97,11 @@
</MudSelect>
</MudTd>
<MudTd DataLabel="Message">
<MudTextField AutoGrow="true" @bind-Value="context.Content.As<ContentText>()!.Text" Label="Your message" Required />
<MudTextField AutoGrow="true" @bind-Value="context.Content.As<ContentText>()!.Text" Label="@T("Your message")" Required />
</MudTd>
</RowEditingTemplate>
<PagerContent>
<MudTablePager PageSizeOptions="[10,20,50,100]" RowsPerPageString="Messages per page" />
<MudTablePager PageSizeOptions="[10,20,50,100]" RowsPerPageString="@T("Messages per page")" />
</PagerContent>
</MudTable>
</MudForm>

View File

@ -81,7 +81,6 @@ public partial class ChatTemplateDialog : MSGComponentBase
private void RemoveMessage(ContentBlock item)
{
this.AdditionalMessages.Remove(item);
// this.Snackbar.Add("Entry removed", Severity.Info);
}
private void AddNewMessageToEnd()
@ -96,7 +95,6 @@ public partial class ChatTemplateDialog : MSGComponentBase
};
this.AdditionalMessages.Add(newEntry);
// this.Snackbar.Add("Initial entry added", Severity.Success);
}
private void AddNewMessageBelow(ContentBlock currentItem)
@ -118,12 +116,10 @@ public partial class ChatTemplateDialog : MSGComponentBase
if (index >= 0)
{
this.AdditionalMessages.Insert(index + 1, newEntry);
// this.Snackbar.Add("New entry added", Severity.Success);
}
else
{
this.AdditionalMessages.Add(newEntry);
// this.Snackbar.Add("New entry added", Severity.Success);
}
}
@ -136,11 +132,6 @@ public partial class ChatTemplateDialog : MSGComponentBase
};
}
private void ItemHasBeenCommitted(object element)
{
// this.Snackbar.Add("Changes saved", Severity.Success);
}
private void ResetItemToOriginalValues(object element)
{
((ContentBlock)element).Role = this.messageEntryBeforeEdit.Role;
@ -200,9 +191,6 @@ public partial class ChatTemplateDialog : MSGComponentBase
private string? ValidateSystemPrompt(string text)
{
// if (string.IsNullOrWhiteSpace(this.DataSystemPrompt))// && string.IsNullOrWhiteSpace(this.DataActions))
// return T("Please enter the system prompt.");
if(text.Length > 444)
return T("The text must not exceed 444 characters.");

View File

@ -32,16 +32,7 @@ public readonly record struct ChatTemplate(uint Num, string Id, string Name, str
if(this.Num == uint.MaxValue)
return string.Empty;
var systemPrompt =
$"""
```
{this.SystemPrompt}
```
""";
return $"""
{systemPrompt}
""";
return this.SystemPrompt;
}
}

View File

@ -198,6 +198,12 @@ public static class ConfigurationSelectDataFactory
yield return new(profile.Name, profile.Id);
}
public static IEnumerable<ConfigurationSelectData<string>> GetChatTemplatesData(IEnumerable<ChatTemplate> chatTemplates)
{
foreach (var chatTemplate in chatTemplates.GetAllChatTemplates())
yield return new(chatTemplate.Name, chatTemplate.Id);
}
public static IEnumerable<ConfigurationSelectData<ConfidenceSchemes>> GetConfidenceSchemesData()
{
foreach (var scheme in Enum.GetValues<ConfidenceSchemes>())

View File

@ -57,4 +57,10 @@ public sealed class DataApp
/// Should we preselect a profile for the entire app?
/// </summary>
public string PreselectedProfile { get; set; } = string.Empty;
/// <summary>
/// Should we preselect a chat template for the entire app?
/// </summary>
public string PreselectedChatTemplate { get; set; } = string.Empty;
}

View File

@ -37,6 +37,11 @@ public sealed class DataChat
/// </summary>
public string PreselectedProfile { get; set; } = string.Empty;
/// <summary>
/// Preselect a chat template?
/// </summary>
public string PreselectedChatTemplate { get; set; } = string.Empty;
/// <summary>
/// Should we preselect data sources options for a created chat?
/// </summary>

View File

@ -265,6 +265,16 @@ public sealed class SettingsManager
preselection = this.ConfigurationData.Profiles.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedProfile);
return preselection != default ? preselection : Profile.NO_PROFILE;
}
public ChatTemplate GetPreselectedChatTemplate(Tools.Components component)
{
var preselection = component.PreselectedChatTemplate(this);
if (preselection != default)
return preselection;
preselection = this.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == this.ConfigurationData.App.PreselectedChatTemplate);
return preselection != default ? preselection : ChatTemplate.NO_CHATTEMPLATE;
}
public ConfidenceLevel GetConfiguredConfidenceLevel(LLMProviders llmProvider)
{

View File

@ -1,5 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using AIStudio.Provider;
using AIStudio.Settings;
using AIStudio.Tools.PluginSystem;
@ -131,4 +130,11 @@ public static class ComponentsExtensions
_ => default,
};
public static ChatTemplate PreselectedChatTemplate(this Components component, SettingsManager settingsManager) => component switch
{
Components.CHAT => settingsManager.ConfigurationData.Chat.PreselectOptions ? settingsManager.ConfigurationData.ChatTemplates.FirstOrDefault(x => x.Id == settingsManager.ConfigurationData.Chat.PreselectedChatTemplate) : default,
_ => default,
};
}